文章目录:
  1. 基本工具
    1. 编译器选项
    2. 文件读入
    3. 快速读入
    4. 快速输出
    5. 常用文件头
  2. STL
    1. 一些函数
    2. 哈希表
    3. 上下界
    4. 优先队列
  3. 数据结构
    1. 二分查找
    2. 并查集
    3. 树状数组(前缀和)
      1. 第k小堆
      2. 可删除堆
  4. 分治
    1. 归并排序
    2. 顺序统计量
    3. 快速幂
  5. 动态规划
    1. 矩阵连乘
    2. 钢管切割
    3. 两流水线
    4. 多流水线
    5. 最长上升子序列
    6. 最长公共子串
    7. 最长公共子序列
    8. 矩阵连乘
    9. 最优搜索树
    10. 0-1 背包问题
    11. 无穷背包问题
    12. 区间dp
    13. 方法种数
  6. 图论算法
    1. DFS
    2. BFS
    3. 链式前向星
    4. 二分图
      1. 判断二分图
      2. 二分图匹配
    5. kruskal
    6. SPFA
    7. Dijkstra
    8. 拓扑排序
    9. Floyd-Warshall
    10. 有向无环图哈密顿
    11. 最大流
    12. 二分图最大匹配
  7. 计算几何
    1. 张正奇的超全版
    2. 线段关系
    3. 凸包
    4. 多边形面积,点是逆时针
    5. 线段相交
    6. 半平面交
    7. 旋转卡壳
  8. FFT
    1. 多项式相乘
    2. 大数相乘
  9. 字符串
    1. KMP
    2. 最长回文子串

基本工具

编译器选项

-std=c++11

文件读入

#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif

快速读入

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

快速输出

inline void Write(int x) {
    static int sta[15];
    if (x<0)    {putchar('-');    x=-x;}
      register int top=0;
      do{
        sta[top++]=x%10,x/=10;
      }while (x);
      while (top) putchar(sta[--top]+48);
}

常用文件头

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+5;

STL

一些函数

// 不自定义比较函数的话记得实现运算符
sort();

// 二分,记得排序,记得实现比较运算符(和同结构体比,和int之类的比),记得排序
// 如果自定了比较函数,则前两个返回第一个不符合比较函数的迭代器,注意返回的是迭代器
lower_bound();
upper_bound();
equel_range();
binary_search()

哈希表

unordered_map<int,int> number_map;
number_map.emplace(array1[i],array2[i]);
number_map[height1[i]];

上下界

const int maxn = 1e5+5;
int num[maxn],res = 0;
int *p = lower_bound(num,num+res,x,greater<int>());
// greater<int> 找第一个小于等于的数的位置
int *p = upper_bound(num,num+res,x,greater<int>());
// greater<int> 找第一个小于的数的位置

优先队列

struct node{
    int u,v;
    bool operator < (const node &a) const{
        return this->v < a.v; // 大顶堆 
        //return this->v > a.v; // 小顶堆 
    }
}; 

int main(){
    priority_queue <int,vector<int>,greater<int> > q_min; // 小顶堆 取出来最小 
    priority_queue <int,vector<int>,less<int> > q_max; //大顶堆 取出来最大 
    priority_queue <node> q;
    
    for(int i=0;i<10;i++){
        q.push({i,20-i});
    } 
    while(!q.empty()){
        node temp = q.top();
        cout << temp.u << " " << temp.v << endl;
        q.pop();
    }
} 

数据结构

二分查找

template <typename T>
int binary_search(const T* array, int n, T value) {
  int left = 0;
  int right = n - 1;
  while (left <= right) {
    int mid = left + (right - left) / 2;
    if (array[mid] == value) {
      return mid;
    } else if (array[mid] < value) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }
  return -1;
}

并查集

int fa[maxn],size[maxn]; // 祖父节点和大小节点,若fa[i]==i,说明i是这个集合的根
inline void init(int n) // 初始化,每个节点是一个集合
{
    for (int i = 1; i <= n; ++i){
        fa[i] = i;
        size[i] = 1;
    }
}
int find(int x) // 查找x节点的集合的根,同时进行路径压缩
{
    return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
inline void merge(int i, int j) // 对i节点和j节点进行合并
{
    size[find(j)] += size[find(i)];
    fa[find(i)] = find(j);
}

树状数组(前缀和)

struct Bit {
    #define Maxn 1000100
    long long val[Maxn];
    inline long long lowbit(int x) {return x & -x;} // 最低位1
    inline void add(int x, long long v) {while (x < Maxn) {val[x] += v; x += lowbit(x);}} // x位添加v
    inline long long ask(int x) {int res = 0; while (x) {res += val[x]; x -= lowbit(x);} return res;} // 返回x位前缀和
    inline long long query(int l, int r) {return ask(r) - ask(l - 1);} // 返回[l, r]区间和
} bit;

第k小堆

struct Priority_Queue {
    int k; 
    priority_queue<int> q1; // 大根堆,当前集合中最小的 k-1 个元素
    priority_queue<int, vector<int>, greater<int> > q2; // 小根堆,保存剩下的所有元素
    Priority_Queue() { }
    Priority_Queue(int _k) : k(_k) {} // 构造方法,应该用这个

    void push(int v) {
        q1.push(v);
        while (q1.size() >= k) q2.push(q1.top()), q1.pop();
    }

    void pop() {
        if (!q2.empty()) q2.pop(); 
        else if(!q1.empty()) q1.pop();
    }

    int top() { // 返回当前第k小
        if (!q2.empty()) return q2.top();
        else if(!q1.empty()) return q1.top();
        else return -1;
    }
};

使用方法:

Priority_Queue pq(3); // 维护第 3 小
pq.push(5);
pq.push(1);
pq.push(7);
// 此时 pq.top() = 当前第 3 小

可删除堆

struct p{
    int data;
    int k;
};

struct H{
    #define N 1000000+5
    #define type p
    type Heap[N];
    int HeapSize = 0;
    
    int cmp(const type &a,const type &b){
        return a.data<=b.data;
    }
    
    void swap(type arr[], int i, int j){
        type temp;
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    } 
    
    void ShiftDown(int i){
        int cur = i;
        while(cur<=HeapSize){
            int left = cur<<1;
            int right = left + 1;
            int min = cur;
            if(left<=HeapSize&&cmp(Heap[left],Heap[min])) min = left;
            if(right<=HeapSize&&cmp(Heap[right],Heap[min])) min = right;
            if(min==cur) return;
            else{
                swap(Heap,cur,min);
                cur = min;
            } 
        }
    } 
    
    void ShiftUp(int i){
        int cur = i;
        while(cur>1){
            int parent = cur>>1;
            if(!cmp(Heap[parent],Heap[cur])){
                swap(Heap,parent,cur);
                cur = parent;
            }
            else return;
        }
    } 
    
    void push(type num){
        Heap[++HeapSize] = num;
        ShiftUp(HeapSize);
    }
    
    void pop(){
        if(HeapSize<=0) return ;
        Heap[1] = Heap[HeapSize--];
        ShiftDown(1);
    }
    // 删除第k个元素
    void del(int k){
        if(HeapSize<=0) return;
        Heap[k] = Heap[HeapSize--];
        ShiftDown(k);
        ShiftUp(k);
    }
    // 修改第k个元素
    void repl(int k,type num){
        if(HeapSize<k) return;
        Heap[k] = num;
        ShiftDown(k);
        ShiftUp(k);
    }
    
    type top(){
        return Heap[1];
    }
};

分治

归并排序

#include <stdio.h>

int temp[10000];
void merge(int low,int mid,int high,int array[]){
    int l = low;
    int r = mid+1;
    int t = low;
    
    while(l<=mid&&r<=high){
        if(array[l]<array[r]){
            temp[t++] = array[l++];
        }
        else{
            temp[t++] = array[r++];
        }
    }
    while(l<=mid) temp[t++] = array[l++];
    while(r<=high) temp[t++] = array[r++];
    for(int i=low;i<=high;i++){
        array[i]=temp[i];
    }
}

void merge_sort(int low, int high,int array[]){
    if(low<high){
        int mid = low + (high-low)/2; //小知识点,如果直接相加可能会溢出 
        merge_sort(low,mid,array);
        merge_sort(mid+1,high,array);
        merge(low,mid,high,array); 
    }
}

int main(){
    int a[5] = {5,4,3,2,1};
    merge_sort(0,4,a);
    for(int i=0;i<5;i++){
        printf("%d ",a[i]);
    }
    return 0;
}

顺序统计量

// 9.2
int random_partition(std::vector<int>& a,int p,int r)
{
    int ra=rand()%(r+1-p)+p;
    std::swap(a[ra],a[r]);
    int x=a[r];
    int i=p-1;
    for(int j=p;j<r;j++)
        // 目前是从小到大
        if(a[j]<=x)
            std::swap(a[++i],a[j]);
    std::swap(a[i+1],a[r]);
    return i+1;
}
// a中从p到人、(均包含)的第i顺序统计量
int random_select(std::vector<int>& a,int p,int r,int i)
{
    if(p==r)
        return a[p];
    int q=random_partition(a,p,r);
    int k=q-p+1;
    if(i==k)
        return a[q];
    else if(i<k)
        return random_select(a,p,q-1,i);
    else 
        return random_select(a,q+1,r,i-k);
}
void simu_min_max(const std::vector<int>& a,
                  int& min,int& max)
{
    int begin_index=1;
    if(a.size()%2==1)
    {
        min=max=a[0];
    }
    else
    {
        min=std::min(a[0],a[1]);
        max=std::max(a[0],a[1]);
        begin_index=2;
 
    }
    for(int i=begin_index;i<a.size();i+=2)
    {
        if(a[i]>a[i+1])
        {
            max=std::max(max,a[i]);
            min=std::min(min,a[i+1]);
        }
        else
        {
            max=std::max(max,a[i+1]);
            min=std::min(min,a[i]);
        }
 
    }
}



/* 数组版 */
int random_partition(int a[],int p,int r)
{
    int ra=rand()%(r+1-p)+p;
    std::swap(a[ra],a[r]);
    int x=a[r];
    int i=p-1;
    for(int j=p;j<r;j++)
        if(a[j]<=x)
            std::swap(a[++i],a[j]);
    std::swap(a[i+1],a[r]);
    return i+1;
}
int random_select(int a[],int p,int r,int i)
{
    if(p==r)
        return a[p];
    int q=random_partition(a,p,r);
    int k=q-p+1;
    if(i==k)
        return a[q];
    else if(i<k)
        return random_select(a,p,q-1,i);
    else 
        return random_select(a,q+1,r,i-k);
}


/* 使用参考 */
#include"chapter9.h"
void main()
{
    int array[]={1,2,3,4,5,6,7,8,9,0};
    std::vector<int> a(array,
        array+sizeof(array)/sizeof(int));
    //int rt=random_select(a,0,9,7);
    //std::cout<<rt<<std::endl;
    int min=0;int max=0;
    simu_min_max(a,min,max);
    std::cout<<"the min="<<min<<
        " ,the max="<<max<<std::endl;
}

快速幂

long long binpow(long long a, long long b) {
  long long res = 1;
  while (b > 0) {
    if (b & 1) res = (res * a);
    a = (a * a);
    b >>= 1;
  }
  return res;
}

动态规划

矩阵连乘

#include <bits/stdc++.h>
using namespace std;

const long long INF = (1LL<<62);

long long p[305];
long long n;
long long m[305][305];

long long MCM(int i, int j){
    if (i == j) return 0;
    if (m[i][j] != INF) return m[i][j];
    long long best = INF;
    for (int k = i; k <= j-1; ++k){
        long long q = MCM(i, k) + MCM(k+1, j) + p[i-1]*p[k]*p[j];
        if (q < best) best = q;
    }
    return m[i][j] = best;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 0; i <= n; ++i) cin >> p[i];

    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= n; ++j){
            m[i][j] = (i==j ? 0 : INF);
        }
    }

    cout << MCM(1, n) << "\n";
    return 0;
}

钢管切割

//钢管切割最小值 
#include<iostream>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    int length,MIN;//分别为长度和最小值 
    while(cin >> length)
    {
        int p[length+1];
        for(int i = 1;i<=length;i++)
            cin >> p[i];//依据题意,i长度的价值 
        
        int price[length+1];//保存每段价值
        price[0] = 0;//会使用到price[0],防止出错初始化 
        
        for(int i = 1;i<=length;i++)
        {
            MIN = 2147483647;//获得最小值时需要设为最大值 
            for(int j = 1;j<=i;j++) 
            {
                if(MIN > p[j]+price[i-j])
                //不断将i长度切割为前j部分和后i-j部分
                //以找到i长度切割后的最小值 
                {
                    MIN = p[j] + price[i-j];//替换最小值 
                    price[i] = MIN;
                }
                //状态转移方程介绍见实现解释 
            }
        }
        cout << price[length] << '\n';
        //输出最长部分(i)切割后的最小值
    }
    return 0;
}

求切割最大值、分割数量和分割方法:

#include <bits/stdc++.h>
using namespace std;

int n;
int p[10009];

long long max, k , a[10009];

long long r[10009], shang[10009]; // r是dp数组代表最大值,shang代表i长度分割出的第一段长度

void fenge(int t){
    if(shang[t]==0){
        a[++k] = t;
        return;
    }
    fenge(shang[t]);
    fenge(t-shang[t]);
}

int main(){
    scanf("%d",&n);
    for(int i = 1; i<=n; i++) scanf("%d",&p[i]);
    for(int i = 1; i<=n; i++){
        shang[i] = 0;
        r[i] = p[i];
        for(int j = 1; j <= i/2; j++){
            long long now = r[j] + r[i-j];
            if(now>r[i]){
                r[i] = now;
                shang[i] = j;
            }
        }
    }
    printf("%lld\n", r[n]);
    k = 0;
    fenge(n);
    printf("%lld\n",k);
    for(int i = 1; i<=k;i++) printf("%lld ",a[i]);
    return 0;
}

两流水线

//流水线调度基本问题 
#include<iostream>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    int n;
    int fend;
    int i;
    while(cin >> n)
    {
        int a[2][n+1],t[2][n];
        //两条线上每一家花费的时间 
        for(i = 1;i<=n;i++)
            cin >> a[0][i];
        for(i = 1;i<=n;i++)
            cin >> a[1][i];
            
        //两条线不同位置转移的时间花费 
        for(i = 1;i<n;i++)
            cin >> t[0][i];
        for(i = 1;i<n;i++)
            cin >> t[1][i];
        
        //存储两条线不同位置的最小时间 
        int f[2][n+1];
        //初始化防止错误 
        f[0][1] = a[0][1];
        f[1][1] = a[1][1];
        
        for(i = 2;i<=n;i++)
        {
            //具体状态转移方程介绍见实现解释 
            if(f[0][i-1]+a[0][i]<f[1][i-1]+t[1][i-1]+a[0][i])
                 f[0][i] = f[0][i-1]+a[0][i];
            else
                f[0][i] = f[1][i-1]+t[1][i-1]+a[0][i];

            if(f[1][i-1]+a[1][i]<f[0][i-1]+t[0][i-1]+a[1][i])
                f[1][i] = f[1][i-1]+a[1][i];
            else
                f[1][i] = f[0][i-1]+t[0][i-1]+a[1][i];
        }
        //计算两条线分别的最后时间花费得最小值 
        if(f[0][n]<f[1][n])
            fend = f[0][n];
        else
            fend = f[1][n];
        cout << fend << '\n';
    }
    return 0;
}

多流水线

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    int num,n;
    int fend;
    int i,j;
    int tempf;
    while(cin >> num >> n)//分别为线路个数和站台个数 
    {
        int a[num][n+1],t[num][num];
        for(i = 0;i<num;i++)
            for(j = 1;j<=n;j++)
                cin >> a[i][j];//i条线j站台的花费 
        
        for(i = 0;i<num;i++)
            for(j = 0;j<num;j++)
                cin >> t[i][j];//i条线到j条线的调度费用 

        int f[num][n+1];//一维为当前线路,二维为站台位置 
        for(i = 0;i<num;i++)
        {
            f[i][1] = a[i][1];//每条线第一个站台的最小花费就是第一个 
        }
        for(i = 2;i<=n;i++)
        {
            for(j = 0;j<num;j++)
            {
                tempf = 2147483647;//为了找到最小值先设定最大的 
                for(int k = 0;k<num;k++)
                {//循环判断所有条道路的情况 
                    if(tempf > f[k][i-1]+t[k][j]+a[j][i])
                    {
                        tempf = f[k][i-1]+t[k][j]+a[j][i];
                    }
                }
                f[j][i] = tempf;//存储j条线在i站台的最小值 
            }
        }
        tempf = f[0][n];
        for(i = 1;i<num;i++)
        {
            if(tempf > f[i][n])
            {
                tempf = f[i][n];
            }
        }
        fend = tempf;//得到最大值 
        cout << fend << '\n';
    }
    return 0;
}

最长上升子序列

#include<iostream>
using namespace std;
int main()
{
    int n;
    cin >> n;
    int a[n],dp[n];
    for(int i = 0;i<n;i++)
    {
        cin >> a[i];
        dp[i] = 1;//自身一定是一个长度的序列 
    }
    for(int i = 1;i<n;i++)
    {
        for(int j = 0;j<i;j++)
        {
            if(a[i] > a[j])//因为是上升,所以需要只有比前面的值大才可能形成最长上升子序列 
            {
                if(dp[i] < dp[j]+1) dp[i] = dp[j] + 1;//记录i处最长的上升序列长度
                //即前面的序列长度最大长度+1即是i处的最大长度
            }
        }
    }
    int max = dp[0];//不一定最后一个是最长的,因此需要获取最大值 
    for(int i = 1;i<n;i++)
    {
        if(max < dp[i]) max = dp[i];
    }
    cout << max << '\n';
    return 0;
}

最长公共子串

void zichuan(){
    
    int mm = 0;
    for(int i = 0; i <l1;i++){
        for(int j = 0; j < l2; j++){
            m[i][j] = 0;
        }
    }
    for(int i = 0; i <l1;i++){
        for(int j = 0; j < l2; j++){
            if(i==0){
                if(s1[i] == s2[j]) m[i][j] = 1;
                continue;
            }if(j==0){
                if(s1[i] == s2[j]) m[i][j] = 1;
                continue;
            }
            m[i][j] = 0;
            if(s1[i] == s2[j]) m[i][j] = m[i-1][j-1] + 1;

        }
    }
    for(int i = 0; i <l1;i++){
        for(int j = 0; j < l2; j++){
            if(m[i][j] > mm) mm = m[i][j];
        }
    }
    printf("%d" ,mm);
}

最长公共子序列

void zixulie(){
    for(int i = 0; i <l1;i++){
        for(int j = 0; j < l2; j++){
            m[i][j] = 0;
        }
    }
    for(int i = 0; i <l1;i++){
        for(int j = 0; j < l2; j++){
            if(i==0){
                if(s1[i] == s2[j]) m[i][j] = 1;
                continue;
            }if(j==0){
                if(s1[i] == s2[j]) m[i][j] = 1;
                continue;
            }
            m[i][j] = m[i-1][j-1];
            if(s1[i] == s2[j]) m[i][j]++;
            if(m[i][j] < m[i-1][j]) m[i][j] = m[i-1][j];
            if(m[i][j] < m[i][j-1]) m[i][j] = m[i][j-1];
        }
    }
    printf(" %d\n",m[l1-1][l2-1]);
    return ;
}

矩阵连乘

#include<iostream>
using namespace std;
int length;
int cut[2000][2000];
int count;
void printCut(int i,int j)
{
    if(i == j)
        cout << 'A' << i;
    else
    {
        cout << '(';
        printCut(i,cut[i][j]);
        printCut(cut[i][j]+1,j);
        count++;
        cout << ')';
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    length = n;
    int m[n+1];
    for(int i = 0;i<=n;i++)
        cin >> m[i];
    
    int num[n+1][n+1];//对应矩阵个数即可
    for(int i = 1;i<=n;i++)
        num[i][i] = 0;
    
    int temp;
    count = 0;
    for(int l = 2;l<=n;l++)
    //l为1的情况不必讨论,因为就是矩阵自己,所以从2到n依次进行 
    { 
        for(int i = 1;i<=n-l+1;i++)
        //从第一个矩阵一直到最后一个可到达的矩阵
        //这样i表示的既是连续l个矩阵的第一个矩阵
        //根据输入,第i个矩阵的行数:i-1的值,列数:i的值 
        {
            int j = i+l-1;//当前l个矩阵的最后一个矩阵下标 
            num[i][j] = 2147483647;//用于比较 
            for(int k = i;k<=j-1;k++)
            //k是节点,代表第k和k+1个矩阵之间的那个下标
            //所以需要到达j-1,也就是第j个矩阵前的那个位置才算结束 
            //由位置可知k的值为第k个矩阵的列数即划分开时需要乘的值 
            {
                temp = num[i][k]+num[k+1][j]+m[i-1]*m[k]*m[j];
                //当前划分状态的计算次数,两个num分别为两侧矩阵链的次数,最后一项为合并后的新增次数 
                if(num[i][j]>temp)
                {
                    num[i][j] = temp;
                    cut[i][j] = k;
                }
            }
        }
    }
    printCut(1,n);
    cout << '\n'; 
    cout << count << '\n';//切割次数 
    cout << num[1][n] << '\n';//计算次数 
    return 0;
}

最优搜索树

#include<iostream>
using namespace std;
const int MAX = 2147483647;
double **root;
int di;
int n;
void printOBST(int l,int r)
{
    if(l == 1&&r == n)
    {
        di = 0;
        cout << "k" << root[1][n] << "为根" << '\n';
    }
    int t,lt,rt;
    t = root[l][r];
    if(l == t)
        cout << "d"<<di++ << "是k"<<t << "的左孩子" << '\n';
    else
        if(l < t)
        {
            lt = root[l][t-1];
            cout << "k"<<lt << "是k"<<t << "的左孩子" << '\n';
            printOBST(l,t-1);
        }
        else return ;
    
    if(r == t)
        cout << "d"<<di++ << "是k"<<t << "的右孩子" << '\n';
    else
        if(r > t)
        {
            rt = root[t+1][r];
            cout << "k"<<rt << "是k"<<t << "的右孩子" << '\n';
            printOBST(t+1,r);
        }
        else return ;
}
int main()
{
    int maxi;
    double temp;
    while(cin >> n)
    {
        double p[n+1],q[n+1];
        for(int i = 1;i<=n;i++)
            cin >> p[i];
        for(int i = 0;i<=n;i++)
            cin >> q[i];
        double e[n+2][n+1];//需要存储q的内容,因此n+2 
        double w[n+2][n+1];//同上 
        root = new double *[n+1];//只是在p中选root因此n+1即可 
        for(int i = 0;i<=n;i++)
            root[i] = new double[n+1];
            
        for(int i = 1;i<=n+1;i++)
        {
            e[i][i-1] = q[i-1];
            w[i][i-1] = q[i-1];
        }
        for(int l = 1;l<=n;l++)
        {
            for(int i = 1;i<=n-l+1;i++)
            {
                maxi = i+l-1;
                //i+l-1就是长度l时能到达的最大的数据下标 
                e[i][maxi] = MAX;
                w[i][maxi] = w[i][maxi-1]+p[maxi]+q[maxi];
                //i--j的总概率和即等于i--j-1的加上新增的maxi处的k和d的概率 
                for(int j = i;j<=maxi;j++)
                {
                    temp = e[i][j-1]+e[j+1][maxi]+w[i][maxi];
                    if(temp < e[i][maxi])
                    {
                        e[i][maxi] = temp;
                        root[i][maxi] = j;
                    }
                }
            }
        }
        cout << "最小搜索期望为:" << e[1][n] << '\n';
        cout << "树的形状如下:" << '\n';
        printOBST(1,n);
    }
    return 0;
}

0-1 背包问题

典型例题:求目标sum,求目标sum的方法数量

#include <bits/stdc++.h>
#define ll long long 
const int maxn = 1e5;
using namespace std;

int n,bagVol;
int value[maxn],weight[maxn];
ll dp[maxn];

int main(){
    // dp[j]表示包括至当前物品,最大容量为j的背包的最优选择
    for(int i=0;i<n;i++){
        for(int j=bagVol;j>=weight[i];j--){
            dp[j] = (ll) max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    printf("%lld",dp[bagVol]);
    return 0;
} 

无穷背包问题

#include <bits/stdc++.h>
#define ll long long 
const int maxn = 1e5;
using namespace std;

int n,bagVol;
int value[maxn],weight[maxn];
ll dp[maxn];

int main(){
    for(int i=0;i<n;i++){
        for(int j=weight[i];j<=bagVol;j++){
            dp[j] = (ll) max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    printf("%lld",dp[bagVol]);
    return 0;
} 

区间dp

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 305;

ll c[maxn][maxn],p[maxn]; // c[i][j] 表示 i-j个矩阵最优解 DP区间
int s[maxn][maxn];

void print(int i,int j){
    if(i==j){
        printf("A%d",i);
        return;
    }
    else{
        printf("(");
        print(i,s[i][j]);
        print(s[i][j]+1,j);
        printf(")");
    }
    
}

int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<=n;i++) scanf("%lld",&p[i]);
    for(int d=2;d<=n;d++){ // 遍历每个区间长度 
        for(int i=1;i<=n-d+1;i++){
            int j = i+d-1;
            c[i][j] = LLONG_MAX;
            for(int k=i;k<j;k++){
                ll newc = c[i][k] + c[k+1][j] + p[i-1]*p[k]*p[j];
                if(newc<c[i][j]){
                    c[i][j] = newc;
                    s[i][j] = k;
                }
            }
        }
    }
    print(1,n);
    //printf("%lld",c[1][n]);
    return 0;
} 

方法种数

dp[j] += dp[j - nums[i]];

图论算法

DFS

bool is_visit[maxn];
void dfs_visit(int v) {
    is_visit[v] = 1;
    // 此处添加访问逻辑
    for(int edge_inx = h[v]; ~edge_inx; edge_inx = ne[edge_inx]) {
        if(!is_visit[e[edge_inx]]) {
            dfs_visit(e[edge_inx]);
        }
    }
    // 访问退出时间戳
}

void dfs(int n) {
    memset(is_visit, 0, sizeof(bool) * (n + 2));
    for(int i = 1; i <= n; i++) {
        if(!is_visit[i]) {
            dfs_visit(i);
        }
    }
}

BFS

bool is_visit[maxn];
void bfs_visit(int v) {
    is_visit[v] = 1;
    queue<int> q;
    q.push(v);
    while (!q.empty()) {
        v = q.front();
        q.pop();
        for(int edge_inx = h[v]; ~edge_inx; edge_inx = ne[edge_inx]) {
            if(!is_visit[e[edge_inx]]) {
                // 访问逻辑
                bfs_visit(e[edge_inx]);
                q.push(e[edge_inx]);
            }
        }
    }
}

void bfs(int n) {
    memset(is_visit, 0, sizeof(bool) * (n + 2));
    for(int i = 1; i <= n; i++) {
        if(!is_visit[i]) {
            bfs_visit(i);
        }
    }
}

链式前向星

//链式前向星
#define maxn 100000
#define maxm 200000

int h[maxn], e[maxm], ne[maxm], cnt;
long long w[maxm];
void add(int a, int b, long long c){
    e[cnt] = b;
    w[cnt] = c;
    ne[cnt] = h[a];
    h[a] = cnt;
    cnt++;
}
int main(){
    memset(h, -1, sizeof h); //初始化 
    int u = 4;// u为起点 
    // 遍历此处结束的标志是不等于-1
    for(int i = h[u]; ~i; i = ne[i]) {
        printf("%d->%d",u,e[i]);
    }
    return 0;
}


/* 另一种写法 */
#define MAX_VERTEX 1000005
#define MAX_EDGE 2000005

//链式前向星所使用的边,数组下标为0处不存数据
struct Edge {
    //目标顶点
    int toV;
    //该顶点的下一个边
    int nextE;
    unsigned long long weight;
}EDGE[MAX_EDGE * 2];
int EDGE_CNT;

void addEdge(int start, int to, unsigned long long weight) {
    //初始值为0,先增加,下标为0处空着
    EDGE_CNT++;
    EDGE[EDGE_CNT].toV = to;
    EDGE[EDGE_CNT].weight = weight;
    EDGE[EDGE_CNT].nextE = HEAD[start];
    HEAD[start] = EDGE_CNT;
}

二分图

判断二分图

int color[maxn],n; 
bool dfs(int u, int c) {
    color[u] = c;
    for(int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if(color[j]) {
            if(color[j] == c) return 0;
        }
        else if(!dfs(j, 3 - c)) return 0;
    }
    return 1;
}
bool check() {
    memset(color, 0, sizeof color);
    for (int i = 1; i <= n; i ++) 
        if(!color[i])
            if(!dfs(i, 1))
                return 0;
    return 1;
}

二分图匹配

int G[maxn][maxn]; 
int vis[maxn],match[maxn];
bool dfs(int node,int m){
    for(int i = 1; i<=m; i++) {
        if(!vis[i]&&G[node][i]){
            vis[i] = true;
            if(!match[i]||dfs(match[i],m)){
                match[i] = node;
                return true;
            }
        }
    }
    return false;
} 
int max_match(int n,int m){ // n 为左边的点的个数 
    int num = 0;
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(dfs(i,m)) num++;
    }
    return num;
}

kruskal

struct Edge
{
    int u,v,w;
}edge[300005];
int f[50005],n,m,cnt;
long long ans;

bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}

int find(int x)
{
    while(x!=f[x]) x=f[x]=f[f[x]];
    return x;
}

void kruskal()
{
    sort(edge,edge+m,cmp);
    for(int i=0;i<m;i++)
    {
        int u=find(edge[i].u);
        int v=find(edge[i].v);
        if(u==v) continue;
        ans+=edge[i].w;
        f[v]=u;
        if(++cnt==n-1) break;
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=0;i<m;i++) scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
    kruskal();
    printf("%lld",ans);
    return 0;
}
//使用边列表保存图,使用数组模拟并查集,允许重边的自环
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<algorithm>

#define MAX_VERTEX 200005
#define MAX_EDGE 500005

using namespace std;

struct Edge {
    int u, v;
    long long w;
}EDGE[MAX_EDGE];

//实现类似并查集的功能
//初始时每个元素值均为其下标,表示自己即为各自集合的根
//如果一个元素的值与其下标不同,即表示该元素被合并到其他集合中了
//但不一定被连接到其他集合的根,需要通过遍历,寻找被链接集合的根
int FIND_A[MAX_VERTEX];

//用于C++sort()的排序函数,返回true时表示元素顺序符合要求
bool edge_cmp(Edge left, Edge right);
//输入已经排序好的边列表,以及图的节点个数,元素个数
//能够处理掉重边和自环
long long kruskal(Edge *, int, int);
//寻找输入的顶点所对应的集合的根
int find(int);

int main() {
    int n, m, u, v;
    long long w;
    
    cin >> n >> m;
    for(int i = 0; i < m; i++) {
        scanf("%d%d%lld", &EDGE[i].u, &EDGE[i].v, &EDGE[i].w);
    }

    sort(EDGE, EDGE + m, edge_cmp);
    
    cout << kruskal(EDGE, n, m);
}

bool edge_cmp(Edge left, Edge right) {
    return left.w < right.w;
}

long long kruskal(Edge *edges, int v_cnt, int e_cnt) {
    //初始化并查集
    for(int i = 0; i < v_cnt; i++) {
        FIND_A[i] = i;
    }

    //记录始点在并查集中的根,终点在并查集中的根
    int a_e_start, a_e_end;
    
    //当已经插入的边的数目 == n-1 时停止
    int r_cnt = 1;
    long long length = 0;

    for(int i = 0; i < e_cnt && r_cnt < v_cnt; i++) {
        a_e_start = find(edges[i].u);
        a_e_end = find(edges[i].v);

        if(a_e_end == a_e_start) {
            continue;
        }
        //这里需要将目标节点集合整个集合合并到源节点集合中,故不能FIND_A[edges[i].v] = a_e_start,这样只能合并一个或一部分节点
        FIND_A[a_e_end] = a_e_start;
        length += edges[i].w;
        r_cnt++;
    }

    return length;
}

int find(int vertex) {
    while(vertex != FIND_A[vertex]) {
        vertex = FIND_A[vertex] = FIND_A[FIND_A[vertex]];
    }
    return vertex;
}

SPFA

//给定一个可能有负环的有向带权图,首先判断其是否有负环,如果没有,给出最短路径
//使用spfa算法,这是bellman_ford算法的改进版
//仍使用链式前向星存储图

#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>

#define MAX_VERTEX 2005
#define MAX_ARC 6005
//题目中给出的数值,用于表示正无穷
#define MAX_DIS 1000000000

using namespace std;

struct Arc {
    int toV;
    long long weight;
    int next_index;
}ARCS[MAX_ARC];
int ARC_CNT;

long long DIS[MAX_VERTEX];
//标记目前该节点是否在队中
bool IS_IN_QUEUE[MAX_VERTEX];

//标记目前到该节点的最短路径所经历的弧数量
int VIA_CNT[MAX_VERTEX];

//链式前向星所使用的列表
int A_HEAD[MAX_VERTEX];

//传入以链式前向星表示的图,图的顶点数以及源顶点
bool spfa(Arc *arcs, int *a_head, int n, int s);
void add_arc(int from, int to, long long weight);

int main() {
    int t, n = 0, m = 0;
    int u, v;
    long long w;
    cin >> t;
    for(int group = 0; group < t; group++) {
        cin >> n >> m;
        ARC_CNT = 0;
        //memset(ARCS, 0, (m + 1) * sizeof(ARCS[0]));
        memset(A_HEAD, 0, (n + 1) * sizeof(A_HEAD[0]));
        for(int arc_line = 0; arc_line < m; arc_line++) {
            scanf("%d%d%lld", &u, &v, &w);
            add_arc(u, v, w);
        }

        if(!spfa(ARCS, A_HEAD, n, 1)) {
            printf("boo how\n");
            continue;
        }

        for(int output_v = 1; output_v <= n; output_v++) {
            printf("%lld ", DIS[output_v]);
        }
        printf("\n");
    }
    return 0;
}

void add_arc(int from, int to, long long weight) {
    ARC_CNT++;
    ARCS[ARC_CNT].toV = to;
    ARCS[ARC_CNT].weight = weight;
    ARCS[ARC_CNT].next_index = A_HEAD[from];
    A_HEAD[from] = ARC_CNT;
}

bool spfa(Arc *arcs, int *a_head, int n, int s) {
    fill(DIS, DIS + n + 1, MAX_DIS);
    memset(VIA_CNT, 0, (n + 1) * sizeof(VIA_CNT[0]));
    memset(IS_IN_QUEUE, 0, (n + 1) * sizeof(IS_IN_QUEUE[0]));

    DIS[s] = 0;
    
    queue<int> q;
    q.push(s);
    IS_IN_QUEUE[s] = 1;

    //该for循环是本题最大的坑点,由于题目要求判断“是否存在负环”,而不是“是否有源顶点能够到达的负环”,故需要将所有顶点入队一次以判断负环
    //最严谨的做法是建立超级源点,以0权重通向所有顶点
    //一般的spfa算法不需要该for循环
    for(int i = 1; i <= n; i++) {
        q.push(i);
        IS_IN_QUEUE[i] = 1;
    }

    int vertex, to;
    long long w;
    while(!q.empty()) {
        vertex = q.front();
        q.pop();
        IS_IN_QUEUE[vertex] = 0;

        for(int arc_ind = a_head[vertex]; arc_ind; arc_ind = arcs[arc_ind].next_index) {
            to = arcs[arc_ind].toV;
            w = arcs[arc_ind].weight;
            
            if(DIS[vertex] + w < DIS[to]) {
                DIS[to] = DIS[vertex] + w;
                VIA_CNT[to] = VIA_CNT[vertex] + 1;

                //当到达该顶点的最短路径包含的弧数达到n时,说明有负环
                if(VIA_CNT[to] >= n) {
                    return false;
                }

                //只有点该顶点的DIS在本轮被更新,才将该顶点入队,考虑松弛以它为始点的边,当然,如果顶点已经在队列中,则不再重复加入
                if(!IS_IN_QUEUE[to]) {
                    q.push(to);
                    IS_IN_QUEUE[to] = 1;
                }
            }
        }
    }
    return true;
}

Dijkstra

#define mp make_pair
#define P pair<int,int>//pair<距离,编号>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 10;
struct node {
    int v,w,link;
}edge[2 * maxn];

int dis[maxn], head[maxn],cnt = 0;//visist数组全为0
priority_queue<P,vector<P>,greater<P> >q;//最小的元素在队首
void init(){
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int w) {
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].link = head[u];
    head[u] = cnt;
    cnt++;
}
void Dij (int n,int s) {
    int visit[maxn];
    for(int i = 1; i <= n; i++)    
    {
        dis[i] = inf;
        visit[i]=0;
    }
    dis[s] = 0; 
    q.push(mp(0,s));
    while(q.size()) {
        int u= q.top().second;
        q.pop();
        if(visit[u]==0)
        {
            visit[u]=1;
            for(int i = head[u];i!=-1; i = edge[i].link) 
            {
                int v=edge[i].v;
                if(dis[v] > dis[u] + edge[i].w) 
                {
                    dis[v] = dis[u] + edge[i].w;
                    q.push(mp(dis[v],v));
                }
            }
        }
    }
}
int main(int argc, char** argv) {
    // add(v,u,w);
    // Dij (n,s);
}
//使用链式前向星保存图,允许环路,重边,但不允许负边
//统计一共有多少条最短路
#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>

#define MAX_VERTEX 1000005
#define MAX_EDGE 1600005

#define MOD 998244353

using namespace std;

//链式前向星所使用的边,数组下标为0处不存数据
struct Edge {
    //目标顶点
    int toV;
    //该顶点的下一个边
    int nextE;
    unsigned long long weight;
}EDGE[MAX_EDGE * 2];
int EDGE_CNT;

//优先队列中顶点集合
struct Vertex {
    int num;
    unsigned long long dis;
    bool operator<(const Vertex &pNext) const{
        return dis > pNext.dis;
    }
};
priority_queue<Vertex> Q;
//在dijkstra算法中,保存目前已经得到的,各个顶点到源点的最短距离
unsigned long long DIS[MAX_VERTEX];
int IS_VISIT[MAX_VERTEX];

//记录连接到对应顶点的边链表的头数组下标,如果值为0表示没有边
int HEAD[MAX_VERTEX];

// 维护目标
int PATH_CNT[MAX_VERTEX];



//为链式前向星链表添加节点,在链表头添加
void addEdge(int, int, unsigned long long);

//给出使用链式前向星保存的边信息,顶点数目,以及源顶点
void dijkstra(Edge *edges, int * head, int n, int s);

int main() {
    int n, m, s, t, u, v;
    cin >> n >> m >> s >> t;

    EDGE_CNT = 0;
    memset(HEAD, 0, sizeof(HEAD[0]) * (n + 1));

    unsigned long long w;
    for(int i = 0; i < m; i++) {
        scanf("%d%d", &u, &v);
        addEdge(u, v, 1);
        addEdge(v, u, 1);
    }

    dijkstra(EDGE, HEAD, n, s);

    printf("%llu", PATH_CNT[t]);
    return 0;
}

void addEdge(int start, int to, unsigned long long weight) {
    //初始值为0,先增加,下标为0处空着
    EDGE_CNT++;
    EDGE[EDGE_CNT].toV = to;
    EDGE[EDGE_CNT].weight = weight;
    EDGE[EDGE_CNT].nextE = HEAD[start];
    HEAD[start] = EDGE_CNT;
}

void dijkstra(Edge *edges, int * head, int n, int s) {
    Vertex temp;
    // 当前节点编号,和目标节点编号
    int t_num, end_v;

    memset(DIS, -1, (n + 1) * sizeof(unsigned long long));

    // 初始化源顶点信息
    DIS[s] = 0;
    Q.push((Vertex){s, 0});
    PATH_CNT[s] = 1;

    while(!Q.empty()) {
        temp = Q.top();
        Q.pop();

        //由于每达到一个顶点,就会对以它为始点的边进行松弛,松弛成功就会把后续点加入队列,因此可能会重复添加
        if(IS_VISIT[temp.num]) {
            continue;
        }
        IS_VISIT[temp.num] = 1;

        t_num = temp.num;

        for(int e_now = head[t_num]; e_now; e_now = edges[e_now].nextE) {
            end_v = edges[e_now].toV;
            if(DIS[end_v] > DIS[t_num] + edges[e_now].weight) {
                DIS[end_v] = DIS[t_num] + edges[e_now].weight;
                // 松弛成功,则刷新目标点最短路径数
                PATH_CNT[end_v] = PATH_CNT[t_num];
                if(!IS_VISIT[end_v]) {
                    Q.push((Vertex){end_v, DIS[end_v]});
                }
            }
            // 如果松弛时发现路径长度与当前最短路长度相同,则将其最短路径数加和
            else if(DIS[end_v] == DIS[t_num] + edges[e_now].weight) {
                PATH_CNT[end_v] += PATH_CNT[t_num];
                PATH_CNT[end_v] %= MOD;
            }
        }
    }
}

拓扑排序

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+5;
int n, m, cnt;
struct Edge
{
    int to, next;
}edge[maxn];
int head[maxn],indegree[maxn];

void add_edge(int u, int v)
{
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
    indegree[v]++;
}

int main()
{
    cin >> n >> m;
    int u, v;
    for (int i = 0; i <= n; i++) head[i] = -1;
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v;
        add_edge(u, v);
    }
    
    priority_queue<int> q;
    for(int i=n;i>=1;i--){
        if(indegree[i]==0){
            q.push(i);
        }
    }
    
    while(!q.empty()){
        int cur = q.top();
        q.pop();
        printf("%d ",cur);
        for (int j = head[cur]; j != -1; j = edge[j].next){
            indegree[edge[j].to]--;
            if(indegree[edge[j].to]==0){
                q.push(edge[j].to);
            }
        }
    }
    return 0;
}
//使用邻接矩阵表示的图,允许有负边、自环、重边
//给出最短路
#include<stdio.h>
#include<string.h>

#define MAXSIZE 10

long long int D0[MAXSIZE][MAXSIZE];
int PI[MAXSIZE][MAXSIZE];

//输入待计算的邻接矩阵,算法执行完毕后二维数组中保存的将是最终结果
void floyd_warshall(long long int d[MAXSIZE][MAXSIZE], int pi[MAXSIZE][MAXSIZE], int n);

//使用floyd_warshell算法计算完毕后,使用该函数输出计算结果
void print_shortest(int u, int v);
void print_vertex(int u, int v);

int main() {
    int n, m, q;
    int u, v;
    long long int temp;

    //这种初始化方式要求各边权值,以及各路径长均小于0x3f3f3f3f3f3f3f3f
    memset(D0, 0x3f, sizeof(D0));
    
    //输入图的顶点数、边数、查询次数
    scanf("%d %d %d", &n, &m, &q);
    for(int i = 0; i < m; i++) {
        scanf("%d %d %lld", &u, &v, &temp);
        //能够同时处理重边
        if(temp < D0[u][v]) {
            D0[u][v] = temp;
            PI[u][v] = u;
        }
    }

    //初始化+处理自环
    for(int i = 1; i <= n; i++) {
        D0[i][i] = 0;
        PI[i][i] = 0;
    }

    floyd_warshall(D0, PI, n);

    for(int i = 0; i < q; i++) {
        scanf("%d %d", &u, &v);
        if(D0[u][v] != 0x3f3f3f3f3f3f3f3f) {
            printf("%lld\n", D0[u][v]);
            print_shortest(u, v);
        }else {
            printf("No So1ution\n");
        }        
    }
}

void floyd_warshall(long long int d[MAXSIZE][MAXSIZE], int pi[MAXSIZE][MAXSIZE], int n) {

    for(int k = 1; k <= n; k++) {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                //注意这里的等于号,去掉等于号不会导致路径长度错误,但会导致求不出来具体路径
                if(d[i][j] <= d[i][k] + d[k][j]) {
                    d[i][j] = d[i][j];
                }else {
                    d[i][j] = d[i][k] + d[k][j];
                    pi[i][j] = pi[k][j];
                    /*
                    该段文字用于简要证明可以直接原表修改元素:

                    首先我们容易观察到:
                    对于每个不同的k值,每次比较操作相当于,在自身以及两元素和之间选择最小值,
                    而这两元素和,分别为(i,j)在第k行和第k列上的投影。

                    其次:
                    对于在第k行和第k列的元素,比较操作不会改变其值。

                    最后:
                    对于第k行和第k列以外的元素,比较操作只会用到自身的值以及第k行和第k列上元素的值,同时只有可能更改自身的值,
                    因而前者不会受其他位置元素的操作影响,而后者在本轮操作中恒定不变。

                    综上:
                    可以原表修改元素。

                    */
                }
            }
        }
    }
}

void print_shortest(int u, int v) {
    if(PI[u][v] || u == v) {
        print_vertex(u, v);
        printf("%d\n", v);
    }else{
        printf("No\n");
    }
}


void print_vertex(int u, int v) {
    if(u == v) {
        return;
    }
    print_vertex(u, PI[u][v]);
    printf("%d ", PI[u][v]);
}

Floyd-Warshall

#include <bits/stdc++.h>
using namespace std;
const long long inf = 1e14;
long long G[505][505];


int main(){
    int n,m,p,u,v,w;
    scanf("%d %d %d",&n,&m,&p);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i!=j) G[i][j] = inf;
            else G[i][j] = 0;
        }
    }
    for(int i=0;i<m;i++){
        scanf("%d %d %d",&u,&v,&w);
        G[u][v] = (long long)min((int)G[u][v],w);
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(G[i][j]>G[i][k]+G[k][j]){
                    G[i][j]=G[i][k]+G[k][j];
                }
            }
        }
    }
    for(int i=0;i<p;i++){
        scanf("%d %d",&u,&v);
        if(G[u][v]>=inf) printf("-1\n");
        else printf("%lld\n",G[u][v]);
    }
    
    
    return 0;
}

有向无环图哈密顿

//判断没有环路没有重边(本算法使用dfs方法允许有重边)的有向图是否存在哈密顿路径
//使用链式前向星保存图
//有机会可以补充一下使用其他方法找寻拓扑排序的算法

#include<stdio.h>
#include<string.h>
#include<string.h>
#include<queue>
#include<algorithm>

#define MAX_VERTEX 100005
#define MAX_ARC 200005

using namespace std;

//链式前向星保存边信息,但没有权重了
struct Arc {
    int toV;
    int next_index;
}ARCS[MAX_ARC];
int ARC_CNT;

//由于递归调用dfs_visit,为了节省栈空间,尽量少传参数
int N;

bool IS_VISIT[MAX_VERTEX];
int A_HEAD[MAX_VERTEX];

//倒序的拓扑排序序列
int TOPOLOGICAL_LIST[MAX_VERTEX];
int TP_CNT;

//使用dfs方法求出拓扑排序,并判断是否有哈密顿路径
bool topological_sort_dfs();

inline void add_arc(int from, int to);

void dfs_visit(int v);

//判断两顶点之间是否有弧
bool has_arc(int from, int to);

int main() {
    int t, u, v, m = -1;
    scanf("%d", &t);
    for(int group = 0; group < t; group++) {
        scanf("%d%d", &N, &m);

        ARC_CNT = 0;
        memset(A_HEAD, 0, (N + 1) * sizeof(A_HEAD[0]));

        for(int arc_line = 0; arc_line < m; arc_line++) {
            scanf("%d%d", &u, &v);
            add_arc(u, v);
        }

        if(topological_sort_dfs()) {
            printf("yy\n");
        }else {
            printf("nn\n");
        }
    }
}

inline void add_arc(int from, int to) {
    ARC_CNT++;
    ARCS[ARC_CNT].toV = to;
    ARCS[ARC_CNT].next_index = A_HEAD[from];
    A_HEAD[from] = ARC_CNT;
}

bool topological_sort_dfs() {
    memset(IS_VISIT, 0, (N + 1) * sizeof(IS_VISIT[0]));
    TP_CNT = 0;

    for(int i = 1; i <= N; i++) {
        dfs_visit(i);
    }

    for(TP_CNT--; TP_CNT; TP_CNT--) {
        if(!has_arc(TOPOLOGICAL_LIST[TP_CNT], TOPOLOGICAL_LIST[TP_CNT - 1])) {
            return 0;
        }
    }
    return 1;
}

void dfs_visit(int v) {
    if(IS_VISIT[v]) {
        return;
    }
    IS_VISIT[v] = 1;
    for(int arc_now = A_HEAD[v]; arc_now; arc_now = ARCS[arc_now].next_index) {
        dfs_visit(ARCS[arc_now].toV);
    }
    // 最后获得倒序的拓扑序列
    TOPOLOGICAL_LIST[TP_CNT++] = v;
}

bool has_arc(int from, int to) {
    for(int arc_now = A_HEAD[from]; arc_now; arc_now = ARCS[arc_now].next_index) {
        if(ARCS[arc_now].toV == to) {
            return 1;
        }
    }
    return 0;
}

最大流

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 10010, E = 200010;

int n, m, s, t; LL ans = 0;
LL cnt = 1, first[N], nxt[E], to[E], val[E];
inline void addE(int u, int v, LL w) {
    to[++cnt] = v;
    val[cnt] = w;
    nxt[cnt] = first[u];
    first[u] = cnt;
}
int dep[N], q[N], l, r;
bool bfs() {//顺着残量网络,方法是 bfs;这是个bool型函数,返回是否搜到了汇点 
    memset(dep, 0, (n + 1) * sizeof(int));//记得开局先初始化,<<<清零大小要注意>>>

    q[l = r = 1] = s;
    dep[s] = 1;
    while(l <= r) {
        int u = q[l++];
        for(int p = first[u]; p; p = nxt[p]) {
            int v = to[p];
            if(val[p] and !dep[v]) {//按照有残量的边搜过去 
                dep[v] = dep[u] + 1;
                q[++r] = v;
            }
        }
    }
    return dep[t];//dep[t] != 0,就是搜到了汇点 
}
LL dfs(int u, LL in/*u收到的支持(不一定能真正用掉)*/) {
//注意,return 的是真正输出的流量
    if(u == t)
        return in;//到达汇点是第一个有效return 
    LL out = 0;
    for(int p = first[u]; p and in; p = nxt[p]) {
        int v = to[p];
        if(val[p] and dep[v] == dep[u] + 1) {//仅允许流向下一层 
            LL res = dfs(v, min(val[p], in)/*受一路上最小流量限制*/);
            //res是v真正输出到汇点的流量
            val[p] -= res;
            val[p ^ 1] += res;
            in -= res;
            out += res;
        }
    }
    if(out == 0)//我与终点(顺着残量网络)不连通 
        dep[u] = 0;//上一层的点请别再信任我,别试着给我流量
    return out;
}
int main() {
    scanf("%d %d %d %d", &n, &m, &s, &t);// s为起点 t为终点编号
    for(int i = 1; i <= m; ++i) {
        int u, v; LL w;
        scanf("%d %d %lld", &u, &v, &w);
        addE(u, v, w);
        addE(v, u, 0);
    }
    while(bfs()) 
        ans += dfs(s, 1e18);
    printf("%lld\n", ans);
    return 0;
}
//使用链式前向星存储图,使用dinic算法计算最大流,加以多路增广和当前弧优化
#include<stdio.h>
#include<queue>
#include<string.h>

#define MAX_VERTEX 105
#define MAX_ARC 5005
#define MAX_VALUE __LONG_LONG_MAX__

using namespace std;

//链式前向星的弧结构体,直接保存残量网络,在读入边时即创建初始的残量网络,保存当前弧上的残余流量
//第一条弧的下标必须是0,这样就不需要保存反向弧下标
struct Arc {
    int toV;
    long long rest;
    int next;
}ARCS[MAX_ARC * 2];
int ARC_CNT, N;

//链式前向星中记录顶点的弧链表的下标
int A_HEAD[MAX_VERTEX];
int CUR[MAX_VERTEX];

//该数组用于保存bfs给出的节点分层,层间的弧以及比目标点还深的顶点将会被忽略
int DEPTH[MAX_VERTEX];



//添加一条单向弧,在本算法实现中必须两条反向的弧一起添加
void add_arc(int from, int to, long long rest);

//输入源顶点以及目标顶点,使用优化dinic算法计算最大流并返回
long long dinic(int s, int t);

//记源顶点为第一层,将各顶点分层,将在到达目标顶点时停止,返回真
//如果残量网络中不存在源顶点到目标顶点的增广路,则返回假
bool bfs(int s, int t);

//给出期望加载给s的入流量,返回在目标点为t的情况下,s最大能接受的流量
//不同于以往的dfs,该dfs在到达某顶点之后,并不会简单将其标记为“以访问”,而是当起不能再接受流量之后再不再访问,即“多路增广”
//当发现本次分层后,某顶点不能再接受更多流量,即不再询问它能接受多少流量,即“当前弧优化”
long long dfs(int s, int t, long long flow);

int main() {
    int t, m, u, v, s, target;
    long long cap;
    scanf("%d", &t);

    for(int group = 0; group < t; group++) {
        scanf("%d%d%d%d", &N, &m, &s, &target);
        ARC_CNT = 0;
        //由于ARCS第一条弧下标为0,故初始值置为-1,表示没有后续弧
        memset(A_HEAD, -1, sizeof(A_HEAD));

        for(int arc_line = 0; arc_line < m; arc_line++) {
            scanf("%d%d%lld", &u, &v, &cap);
            //必须同时添加原弧以及反向弧,使得可以直接通过将边下标抑或1获取反向边
            add_arc(u, v, cap);
            add_arc(v, u, 0);
        }

        printf("%lld\n", dinic(s, target));
    }
}




void add_arc(int from, int to, long long rest) {
    ARCS[ARC_CNT].toV = to;
    ARCS[ARC_CNT].rest = rest;
    ARCS[ARC_CNT].next = A_HEAD[from];
    A_HEAD[from] = ARC_CNT;
    ARC_CNT++;
}

long long dinic(int s, int t) {
    long long result = 0;
    while(bfs(s, t)) {
        result += dfs(s, t, MAX_VALUE);
    }
    return result;
}

bool bfs(int s, int t) {
    memset(DEPTH, 0, sizeof(DEPTH));
    queue<int> q;
    int from, to;

    DEPTH[s] = 1;
    q.push(s);
    CUR[s] = A_HEAD[s];

    while(!q.empty()) {
        from = q.front();
        q.pop();

        for(int arc_now = A_HEAD[from]; ~arc_now; arc_now = ARCS[arc_now].next) {
            to = ARCS[arc_now].toV;
            if(ARCS[arc_now].rest && !DEPTH[to]) {
                DEPTH[to] = DEPTH[from] + 1;
                CUR[to] = A_HEAD[to];
                if(to == t) {
                    return 1;
                }
                q.push(to);
            }
        }
    }
    return false;
}

long long dfs(int from, int t, long long flow) {
    if(from == t || !flow) {
        return flow;
    }
    long long rest = flow, acceptable;
    int to;

    for(int arc_now = CUR[from]; ~arc_now && rest; arc_now = ARCS[arc_now].next){
        to = ARCS[arc_now].toV;
        // 这个地方是关键优化,搜索到当前弧表示之前的都没了
        CUR[from] = arc_now;
        if(ARCS[arc_now].rest && DEPTH[to] == DEPTH[from] + 1) {
            acceptable = dfs(to, t, min(rest, ARCS[arc_now].rest));
            if(!acceptable) {
                DEPTH[to] = 0;
            }
            rest -= acceptable;
            ARCS[arc_now].rest -= acceptable;
            //反向弧
            ARCS[arc_now ^ 1].rest += acceptable;
        }
    }
    //rest为剩下的流量,flow - rest即为能够接受的流量
    return flow - rest;
}

二分图最大匹配

#include <cstdio>
#include <vector>

const int maxn = 1005;

int n, m, t;
int mch[maxn], vistime[maxn];

std::vector<int> e[maxn];

bool dfs(const int u, const int tag);

int main() {
  scanf("%d %d %d", &n, &m, &t);
  for (int u, v; t; --t) {
    scanf("%d %d", &u, &v);
    e[u].push_back(v);
  }
  int ans = 0;
  for (int i = 1; i <= n; ++i) if (dfs(i, i)) {
    ++ans;
  }
  printf("%d\n", ans);
}

bool dfs(const int u, const int tag) {
  if (vistime[u] == tag) return false;
  vistime[u] = tag;
  for (auto v : e[u]) if ((mch[v] == 0) || dfs(mch[v], tag)) {
    mch[v] = u;
    return true;
  }
  return false;
}

计算几何

张正奇的超全版

#include <bits/stdc++.h>
using namespace std;
#define inf 1e100
#define eps 1e-8
//用于浮点数正负判断,根据题目精度修改
const double pi = acos(-1.0); //圆周率
int sgn(double x)
{
    if (fabs(x) < eps)
        return 0;
    if (x < 0)
        return -1;
    return 1;
} //判断浮点数正负
double sqr(double x) { return x * x; } //距离等运算涉及大量平方,简便
//使用Point时注意部分函数是返回新Point而非修改本身值
struct Point
{
    double x, y;
    /*构造函数*/
    Point() {}
    Point(double xx, double yy)
    {
        x = xx;
        y = yy;
    }
    /*重载一些点的基础运算符*/
    bool operator==(Point b) const
    {
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
    }
    bool operator<(Point b) const
    {
        return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
    }
    Point operator-(const Point &b) const
    {
        return Point(x - b.x, y - b.y);
    }
    Point operator+(const Point &b) const
    {
        return Point(x + b.x, y + b.y);
    }
    Point operator*(const double &k) const
    {
        return Point(x * k, y * k);
    }
    Point operator/(const double &k) const
    {
        return Point(x / k, y / k);
    }
    //叉积
    double operator^(const Point &b) const
    {
        return x * b.y - y * b.x;
    }
    //点积
    double operator*(const Point &b) const
    {
        return x * b.x + y * b.y;
    }
    /*当前点为p,求角apb大小*/
    double rad(Point a, Point b)
    {
        Point p = *this;
        return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
    }
    /*逆时针旋转90度*/
    Point rotleft()
    {
        return Point(-y, x);
    }
    /*顺时针旋转90度*/
    Point rotright()
    {
        return Point(y, -x);
    }
    //两点距离
    double dis(Point p)
    {
        return sqrt(sqr(x - p.x) + sqr(y - p.y));
    }
    //原点距离
    double abs()
    {
        return sqrt(abs2());
    }
    double abs2()
    {
        return sqr(x) + sqr(y);
    }
    //改变向量长度
    Point trunc(double r)
    {
        double l = abs();
        if (!sgn(l))
            return *this;
        r /= l;
        return Point(x * r, y * r);
    }
    //单位化
    Point unit() { return *this / abs(); }
    // IO
    void input()
    {
        scanf("%lf%lf", &x, &y);
    }
    void output()
    {
        printf("%.7f %.7f\n", x, y);
    }
    //绕着p点逆时针旋转angle
    Point rotate(Point p, double angle)
    {
        Point v = (*this) - p;
        double c = cos(angle), s = sin(angle);
        return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
    }
};
struct Line
{
    //两点确定直线
    Point s, e;
    Line() {}
    Line(Point ss, Point ee)
    {
        s = ss;
        e = ee;
    }
    void input()
    {
        s.input();
        e.input();
    }
    //点在线段上
    bool checkPS(Point p)
    {
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;
    }
    //直线平行
    bool parallel(Line v)
    {
        return sgn((e - s) ^ (v.e - v.s)) == 0;
    }
    //点和直线关系
    // 1  在左侧
    // 2  在右侧
    // 3  在直线上
    int relation(Point p)
    {
        int c = sgn((p - s) ^ (e - s));
        if (c < 0)
            return 1;
        else if (c > 0)
            return 2;
        else
            return 3;
    }
    //线段相交
    // 2 规范相交
    // 1 非规范相交
    // 0 不相交
    int checkSS(Line v)
    {
        int d1 = sgn((e - s) ^ (v.s - s));
        int d2 = sgn((e - s) ^ (v.e - s));
        int d3 = sgn((v.e - v.s) ^ (s - v.s));
        int d4 = sgn((v.e - v.s) ^ (e - v.s));
        if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)
            return 2;
        return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
               (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
               (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
               (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
    }
    //直线和线段相交
    // 2 规范相交
    // 1 非规范相交
    // 0 不相交
    int checkLS(Line v)
    {
        int d1 = sgn((e - s) ^ (v.s - s));
        int d2 = sgn((e - s) ^ (v.e - s));
        if ((d1 ^ d2) == -2)
            return 2;
        return (d1 == 0 || d2 == 0);
    }
    //两直线关系
    // 0 平行
    // 1 重合
    // 2 相交
    int checkLL(Line v)
    {
        if ((*this).parallel(v))
            return v.relation(s) == 3;
        return 2;
    }
    //直线交点
    Point isLL(Line v)
    {
        double a1 = (v.e - v.s) ^ (s - v.s);
        double a2 = (v.e - v.s) ^ (e - v.s);
        return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
    }
    //点到直线的距离
    double disPL(Point p)
    {
        return fabs((p - s) ^ (e - s)) / (s.dis(e));
    }
    //点到线段的距离
    double disPS(Point p)
    {
        if (sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
            return min(p.dis(s), p.dis(e));
        return disPL(p);
    }
    //两线段距离
    double disSS(Line v)
    {
        return min(min(disPS(v.s), disPS(v.e)), min(v.disPS(s), v.disPS(e)));
    }
    //点在直线上投影
    Point proj(Point p)
    {
        return s + (((e - s) * ((e - s) * (p - s))) / ((e - s).abs2()));
    }
    //向垂直有向直线的左侧移动x
    Line push(double x)
    {
        Point tmp = e - s;
        tmp = tmp.rotleft().trunc(x);
        Point ss = s + tmp;
        Point ee = e + tmp;
        return {ss, ee};
    }
};

//多边形面积,需保证A逆时针
double area(vector<Point> A)
{
    double ans = 0;
    for (int i = 0; i < A.size(); i++)
        ans += (A[i] ^ A[(i + 1) % A.size()]);
    return ans / 2;
}
int contain(vector<Point> A, Point q)
{ // 2 内部 1 边界 0 外部
    int pd = 0;
    A.push_back(A[0]);
    for (int i = 1; i < A.size(); i++)
    {
        Point u = A[i - 1], v = A[i];
        if (Line(u, v).checkPS(q))
            return 1;
        if (sgn(u.y - v.y) > 0)
            swap(u, v);
        if (sgn(u.y - q.y) >= 0 || sgn(v.y - q.y) < 0)
            continue;
        if (sgn((u - v) ^ (q - v)) < 0)
            pd ^= 1;
    }
    return pd << 1;
}
//凸包
vector<Point> ConvexHull(vector<Point> A, int flag = 1)
{ // flag=0 不严格 flag=1 严格
    int n = A.size();
    vector<Point> ans(n * 2);
    sort(A.begin(), A.end());
    int now = -1;
    for (int i = 0; i < A.size(); i++)
    {
        while (now > 0 && sgn((ans[now] - ans[now - 1]) ^ (A[i] - ans[now - 1])) < flag)
            now--;
        ans[++now] = A[i];
    }
    int pre = now;
    for (int i = n - 2; i >= 0; i--)
    {
        while (now > pre && sgn((ans[now] - ans[now - 1]) ^ (A[i] - ans[now - 1])) < flag)
            now--;
        ans[++now] = A[i];
    }
    ans.resize(now);
    return ans;
}
//凸包周长
double convexC(vector<Point> A)
{
    double ans = 0;
    for (int i = 0; i < A.size() - 1; i++)
    {
        ans += A[i].dis(A[i + 1]);
    }
    ans += A[A.size() - 1].dis(A[0]);
    return ans;
}

//凸包直径(最远点对)
double convexDiameter(vector<Point> A)
{
    int now = 0, n = A.size();
    double ans = 0;
    for (int i = 0; i < A.size(); i++)
    {
        now = max(now, i);
        while (1)
        {
            double k1 = A[i].dis(A[now % n]), k2 = A[i].dis(A[(now + 1) % n]);
            ans = max(ans, max(k1, k2));
            if (k2 > k1)
                now++;
            else
                break;
        }
    }
    return ans;
}

// 最近点对, 先要按照 x 坐标排序
double closepoint(vector<Point> &A, int l, int r)
{
    if (r - l <= 5)
    {
        double ans = 1e20;
        for (int i = l; i <= r; i++)
            for (int j = i + 1; j <= r; j++)
                ans = min(ans, A[i].dis(A[j]));
        return ans;
    }
    int mid = l + r >> 1;
    double ans = min(closepoint(A, l, mid), closepoint(A, mid + 1, r));
    vector<Point> B;
    for (int i = l; i <= r; i++)
        if (abs(A[i].x - A[mid].x) <= ans)
            B.push_back(A[i]);
    sort(B.begin(), B.end(), [&](Point k1, Point k2)
         { return k1.y < k2.y; });
    for (int i = 0; i < B.size(); i++)
        for (int j = i + 1; j < B.size() && B[j].y - B[i].y < ans; j++)
            ans = min(ans, B[i].dis(B[j]));
    return ans;
}

int main(){
    int n=getint();
    for(int i=0;i<n;i++){
        double x=getdb(),y=getdb();
        p.push_back((Point){x,y});
    }
    ans=ConvexHull(p,1);
    printf("%.6f\n",convexDiameter(ans));
    return 0;
}

线段关系

inline double Min(double a,double b){return (a<b)?a:b;}
inline double Max(double a,double b){return (a>b)?a:b;}
inline double Fabs(double x){return (x<-EPS)?-x:(x>EPS)?x:0;}
inline bool Equ(double x,double y){return (Fabs(x-y)<EPS);}

inline int Sgn(double x){
    if (Fabs(x)==0)    return 0;
    if (x>0)    return 1;    else return -1;
}

struct Vector{
    double x,y;
    void Clean(void){x=y=0;}
    void Write(void){printf("%.3lf %.3lf\n",x+EPS,y+EPS);}
    Vector operator +(Vector p){return (Vector){x+p.x,y+p.y};}
    Vector operator -(Vector p){return (Vector){x-p.x,y-p.y};}
    double operator *(Vector p){return x*p.y-y*p.x;}
    double operator &(Vector p){return x*p.x+y*p.y;}
};
typedef Vector V;

struct Point{
    double x,y;
    void Clean(void){x=y=0.0;}
    void Write(void){printf("%.3lf %.3lf\n",x+EPS,y+EPS);}
    bool operator ==(Point a){return (Equ(x-a.x,0) && Equ(y-a.y,0));}
    Point operator +(Point p){return (Point){x+p.x,y+p.y};}
    Point operator /(double l){return (Point){x/l,y/l};}
    Vector operator -(Point p){return (Vector){x-p.x,y-p.y};}
};
typedef Point P;

inline void SW(P &a,P &b){P t=a;    a=b;    b=t;}
inline double Dis2(P a,P b){return pow(a.x-b.x,2)+pow(a.y-b.y,2);}
inline double Dis(P a,P b){return pow(pow(a.x-b.x,2)+pow(a.y-b.y,2),0.5);}

struct Circle{
    Point o;
    double r;
    void Clean(void){o.x=o.y=0.0;}
    void Write(void){printf("%.3lf %.3lf %.3lf\n",o.x,o.y,r);}
    bool operator &(Circle a){return (Dis(o,a.o)+r <= a.r+EPS);}
};
typedef Circle C;

inline double Convex_Size(P a[],int n){
    register int i=0,t=0;
    double s=0;
    P O;
    O.x=O.y=0;
    for (i=1;i<=n;i++){
        t=(i<n)?i+1:1;
        s+=(a[i]-O)*(a[t]-O);
    }
    return s/2;
}

inline bool Point_On_Seg(P x,P y,P z){
    double s;
    s=(x-y)*(x-z);
    if (Fabs(s)>EPS)    return 0;
    if (Min(y.x,z.x)-EPS>x.x||Max(y.x,z.x)+EPS<x.x)    return 0;
    if (Min(y.y,z.y)-EPS>x.y||Max(y.y,z.y)+EPS<x.y)    return 0;
    return 1;
}

inline bool Seg_Cross(P a,P b,P c,P d){
    if (Point_On_Seg(a,c,d) || Point_On_Seg(b,c,d) || Point_On_Seg(c,a,b) || Point_On_Seg(d,a,b))    return 1;
    if (Sgn((b-a)*(c-a)) * Sgn((b-a)*(d-a)) >= 0)    return 0;
    if (Sgn((d-c)*(a-c)) * Sgn((d-c)*(b-c)) >= 0)    return 0;
    return 1;
}

凸包

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5; 

int n;
struct ben
{
    double x,y;
}p[maxn],s[maxn];
double check(ben a1,ben a2,ben b1,ben b2)//检查叉积是否大于0,如果是a就逆时针转到b 
{
    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}
double d(ben p1,ben p2)//两点间距离。。。 
{
    return sqrt((p2.y-p1.y)*(p2.y-p1.y)+(p2.x-p1.x)*(p2.x-p1.x));
}
bool cmp(ben p1,ben p2)//排序函数,这个函数别写错了,要不然功亏一篑 
{
    double tmp=check(p[1],p1,p[1],p2);
    if(tmp>0) 
        return 1;
    if(tmp==0&&d(p[0],p1)<d(p[0],p2)) 
        return 1;
    return 0;
}
int main()
{
    
    scanf("%d",&n);
    double mid;
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&p[i].x,&p[i].y);
        if(i!=1&&p[i].y<p[1].y)//这是找最低点 
        {
            mid=p[1].y;p[1].y=p[i].y;p[i].y=mid;
            mid=p[1].x;p[1].x=p[i].x;p[i].x=mid;
        }
    } 
    sort(p+2,p+1+n,cmp);//系统快排 
    s[1]=p[1];//最低点一定在凸包里 
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
        while(cnt>1&&check(s[cnt-1],s[cnt],s[cnt],p[i])<=0) //判断前面的会不会被踢走,如果被踢走那么出栈
            cnt--;
        cnt++;
        s[cnt]=p[i];
    }
    s[cnt+1]=p[1];//最后一个点回到凸包起点
    double ans=0; 
    for(int i=1;i<=cnt;i++) 
        ans+=d(s[i],s[i+1]);//然后s里存好了凸包序列,只需要把两两距离累加就行
    printf("%.2lf\n",ans);
    return 0;
}

多边形面积,点是逆时针

#include <bits/stdc++.h>
using namespace std;

struct point{
    int x;
    int y;
};

point a[105];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d %d",&a[i].x,&a[i].y);
    }
    int res = 0;
    a[n].x=a[0].x;
    a[n].y=a[0].y;
    for(int i=0;i<n;i++)
    {
        res+=(a[i].x*a[i+1].y-a[i+1].x*a[i].y);
    }
    printf("%d",res/2);

    return 0;
}

线段相交

#include <bits/stdc++.h>
using namespace std;

struct Line {
    int x1;
    int y1;
    int x2;
    int y2;
};
 
bool intersection(const Line &l1, const Line &l2)
{
    //快速排斥实验
    if ((l1.x1 > l1.x2 ? l1.x1 : l1.x2) < (l2.x1 < l2.x2 ? l2.x1 : l2.x2) ||
        (l1.y1 > l1.y2 ? l1.y1 : l1.y2) < (l2.y1 < l2.y2 ? l2.y1 : l2.y2) ||
        (l2.x1 > l2.x2 ? l2.x1 : l2.x2) < (l1.x1 < l1.x2 ? l1.x1 : l1.x2) ||
        (l2.y1 > l2.y2 ? l2.y1 : l2.y2) < (l1.y1 < l1.y2 ? l1.y1 : l1.y2))
    {
        return false;
    }
    //跨立实验
    if ((((l1.x1 - l2.x1)*(l2.y2 - l2.y1) - (l1.y1 - l2.y1)*(l2.x2 - l2.x1))*
        ((l1.x2 - l2.x1)*(l2.y2 - l2.y1) - (l1.y2 - l2.y1)*(l2.x2 - l2.x1))) > 0 ||
        (((l2.x1 - l1.x1)*(l1.y2 - l1.y1) - (l2.y1 - l1.y1)*(l1.x2 - l1.x1))*
        ((l2.x2 - l1.x1)*(l1.y2 - l1.y1) - (l2.y2 - l1.y1)*(l1.x2 - l1.x1))) > 0)
    {
        return false;
    }
    return true;
}

Line L[1005];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&L[i].x1,&L[i].y1,&L[i].x2,&L[i].y2);
    }
    int res = 0;
    for(int i=0;i<n-1;i++){
        for(int j=i+1;j<n;j++){
            res += intersection(L[i],L[j]);
        }
    }
    printf("%d",res);
}

半平面交

#include <bits/stdc++.h>
using namespace std;
const int N=510;
struct Node
{
    double x,y;
    Node operator - (const Node A) {return (Node){x-A.x,y-A.y};}
    Node operator + (const Node A) {return (Node){x+A.x,y+A.y};}
    Node operator * (double t) {return (Node){x*t,y*t};}
    double crs(Node A) {return x*A.y-y*A.x;}
}A[N],B[N],C[N];
int n,m,tt,node;
void Add(Node a1,Node a2,Node b1,Node b2)
{
    Node a=a2-a1,b=b2-b1,c=b1-a1;
    double t=b.crs(c)/b.crs(a);
    C[++tt]=a1+a*t;
}
void Cut(Node a,Node b)
{
    tt=0;A[node+1]=A[1];
    for(int i=1;i<=node;i++)
        if((a-A[i]).crs(b-A[i])>=0)
        {
            C[++tt]=A[i];
            if((a-A[i+1]).crs(b-A[i+1])<0)
                Add(A[i],A[i+1],a,b);
        }
        else if((a-A[i+1]).crs(b-A[i+1])>=0)
            Add(A[i],A[i+1],a,b);
    for(int i=1;i<=tt;i++) A[i]=C[i];
    node=tt;
}
double CalcS()
{
    double res=0;
    for(int i=2;i<node;i++) res+=(A[i]-A[1]).crs(A[i+1]-A[1]);
    return res/2;
}
int main()
{
    cin>>n>>m;node=m;n--;
    for(int i=1;i<=m;i++) cin>>A[i].x>>A[i].y;
    while(n--)
    {
        cin>>m>>B[1].x>>B[1].y;B[m+1]=B[1];
        for(int i=2;i<=m;i++) cin>>B[i].x>>B[i].y;
        for(int i=1;i<=m;i++) Cut(B[i],B[i+1]);
    }
    return printf("%.3f\n",CalcS()),0;
}

旋转卡壳

//旋转卡壳求凸包直径,改改能干别的
double rotatingCalipersConvexDiameter(vector<Point> A)
{
    int now = 0, n = A.size();
    double ans = 0;
    A.push_back(A[0]);
    for (int i = 0; i < n; i++)
    {
        while (((A[i + 1] - A[i]) ^ (A[now] - A[i])) < ((A[i + 1] - A[i]) ^ (A[now + 1] - A[i]))) now = (now + 1) % n;
        ans = max(ans, max(A[i].dis(A[now]), A[i + 1].dis(A[now])));
    }
    return ans;
}

FFT

多项式相乘

#include <bits/stdc++.h>
using namespace std;
#define Maxn 1350000
const double Pi=acos(-1);
inline int read()
{
  register char ch=0;
  while(ch<48||ch>57)ch=getchar();
  return ch-'0';
}
int n,m;
struct CP
{
  CP (double xx=0,double yy=0){x=xx,y=yy;}
  double x,y;
  CP operator + (CP const &B) const
  {return CP(x+B.x,y+B.y);}
  CP operator - (CP const &B) const
  {return CP(x-B.x,y-B.y);}
  CP operator * (CP const &B) const
  {return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
}f[Maxn<<1];//只用了一个复数数组 
int tr[Maxn<<1];
void fft(CP *f,bool flag)
{
  for (int i=0;i<n;i++)
    if (i<tr[i])swap(f[i],f[tr[i]]);
  for(int p=2;p<=n;p<<=1){
    int len=p>>1;
    CP tG(cos(2*Pi/p),sin(2*Pi/p));
    if(!flag)tG.y*=-1;
    for(int k=0;k<n;k+=p){
      CP buf(1,0);
      for(int l=k;l<k+len;l++){
        CP tt=buf*f[len+l];
        f[len+l]=f[l]-tt;
        f[l]=f[l]+tt;
        buf=buf*tG;
      }
    }
  }
}
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=0;i<=n;i++)f[i].x=read();
  for (int i=0;i<=m;i++)f[i].y=read();
  for(m+=n,n=1;n<=m;n<<=1);
  for(int i=0;i<n;i++)
    tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
  fft(f,1);
  for(int i=0;i<n;++i)f[i]=f[i]*f[i];
  fft(f,0);
  for(int i=0;i<=m;++i)
    printf("%d ",(int)(f[i].y/n/2+0.49));
  return 0;
}

大数相乘

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

const int N = 1e6;

const double PI = acos(-1);

int n, m;
int res, ans[N];
int AA, BB;
int Lim = 1;//
int L;//二进制的位数
int R[N];

inline int read()
{
    register int x = 0, f = 1;
    register char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-')f = -1;ch =getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 1- + ch - '0';ch = getchar();}
    return x * f;
}

struct Complex
{
    double x, y;
    Complex (double x = 0, double y = 0) : x(x), y(y) { }
}A[N], B[N];
Complex operator * (Complex J, Complex Q) {
    //模长相乘,幅度相加
    return Complex(J.x * Q.x - J.y * Q.y, J.x * Q.y + J.y * Q.x);
}
Complex operator - (Complex J, Complex Q) {
    return Complex(J.x - Q.x, J.y - Q.y);
}
Complex operator + (Complex J, Complex Q) {
    return Complex(J.x + Q.x, J.y + Q.y);
}

string s1, s2;

inline void FFT(Complex *J, double type)
{
    for(int i = 0; i < Lim; ++ i) {
        if(i < R[i]) swap(J[i], J[R[i]]);
        //i小于R[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
    }
    //从底层往上合并
    for(int mid = 1; mid < Lim; mid <<= 1) {//待合并区间长度的一半,最开始是两个长度为1的序列合并,mid = 1;
        Complex wn(cos(PI / mid), type * sin(PI / mid));//单位根w_n^i;
        for(int len = mid << 1, pos = 0; pos < Lim; pos += len) {
        //for(int pos = 0; pos < Lim; pos += (mid << 1)) {
            //len是区间的长度,pos是当前的位置,也就是合并到了哪一位
            Complex w(1, 0);//幂,一直乘,得到平方,三次方...
            for(int k = 0; k < mid; ++ k, w = w * wn) {
                //只扫左半部分,得到右半部分的答案,w 为 w_n^k
                Complex x = J[pos + k];//左半部分
                Complex y = w * J[pos + mid + k];//右半部分
                J[pos + k] = x + y;//蝴蝶变换
                J[pos + mid + k] = x - y;
            }
        }
    }
}

int cnt_a, cnt_b;

int main()
{
    cin >> s1 >> s2;
    n = s1.length();
    m = s2.length();
    //相当于x=10 的多项式
    //读入的数的每一位看成多项式的一项,保存在复数的实部
    for (int i = n - 1; i >= 0; -- i) A[cnt_a ++ ].x = s1[i] - 48;
    for (int i = m - 1; i >= 0; -- i) B[cnt_b ++ ].x = s2[i] - 48;
    while(Lim < n + m) Lim <<= 1, L ++ ;
    //Lim = 1 << int(log2(n + m) + 1); // 补成2的整次幂,这里补成了2的整次幂,所以最后输出答案的时候要除以Lim
    for (int i = 0; i <= Lim; ++ i) {
        //换成二进制序列
        R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
        // 在原序列中 i 与 i/2 的关系是 : i可以看做是i/2的二进制上的每一位左移一位得来
        // 那么在反转后的数组中就需要右移一位,同时特殊处理一下奇数
    }
    //FFT 把a的系数表示转化为点值表示
    FFT(A, 1);
    //FFT 把b的系数表示转化为点值表示
    FFT(B, 1);

    for (int i = 0; i <= Lim; ++ i) 
    //对应项相乘,O(n)得到点值表示的多项式的解C,利用逆变换完成插值得到答案C的点值表示
        A[i] = A[i] * B[i];
    //IFFT 把这个点值表示转化为系数表示
    FFT(A, -1);
    int tot = 0;
    //保存答案的每一位(注意进位)
    for (int i = 0; i <= Lim; ++ i) {
        //取实数四舍五入,此时虚数部分应当为0或由于浮点误差接近0
        ans[i] += (int) (A[i].x / Lim + 0.5);
        if(ans[i] >= 10) {
            ans[i + 1] += ans[i] / 10;
            ans[i] %= 10;
            Lim += (i == Lim);//进一位
        }
    }
    //删掉前导零
    while(!ans[Lim] && Lim >= 1) Lim -- ;
    Lim ++ ;
    //输出
    while(-- Lim >= 0) cout << ans[Lim];
    puts("");
    return 0;
}

字符串

KMP

#include <bits/stdc++.h>
const int maxn = 1e5;
using namespace std;

int ne[maxn];
char pattern[maxn],text[maxn];

void GetNext(char p[],int nex[],int n)
{
    nex[0] = -1;
    int k = -1;
    int j = 0;
    while (j < n - 1)
    {
        //p[k]表示前缀,p[j]表示后缀
        if (k == -1 || p[j] == p[k]) 
        {
            ++k;
            ++j;
            nex[j] = k;
        }
        else 
        {
            k = nex[k];
        }
    }
}

int KmpSearch(int m,int n)
{
    GetNext(pattern,ne,n);
    
    int pLen = n;
    int sLen = m;
    int i = 0;
    int j = 0;
    
    while(i<sLen){
        if(j==n-1&&text[i]==pattern[j]){
            printf("found the number  %d\n",i-j);
        }
        if(text[i]==pattern[j]){
            i++;
            j++;
        }
        else{
            j = ne[j];
            if(j==-1){
                i++;
                j++;
            }
        }
    }
    
}

int main(){
    char t[] = "qwqwqwABABCABAAqABABCABAA";
    char p[]= "ABABCABAA";
    
    for(int i=0;i<100;i++){
        pattern[i] = p[i];
        text[i] = t[i];
    }
    KmpSearch(40,9); 
    
    
    return 0;
}

最长回文子串

#include<cstdio>
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

char STR[1000];

int change(char *str, int i, int j) {
    // (1)i == st, j == st;
    // (2) i == st, j == st + 1;
    int len = strlen(str);
    while(str[i] == str[j] && i >= 0 && j < len) {
        i --;
        j ++;
    }
    return j - i -1;
} 
int main()
{
    int len, t;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", STR);
        len = strlen(STR);
        if(len == 0 ) {
            printf("0\n");
            return 0;
        }
        int maxLen = 1;
        for(int st = 0; st < len - 1; st ++) {
            int len1 = change(STR, st, st + 1);
            int len2 = change(STR, st, st);
            maxLen = max(maxLen, max(len1, len2));
        }
        
        printf("%d\n",maxLen);
    }
    
 }