CSP-S提高組資料結構演算法模板大合集

Star_F發表於2024-07-28

CSP-S 演算法總結

  • 2.2.1 基礎知識與程式設計環境

  • 2.2.2 C++ 程式設計 2

    • set/nultiset
    • map/multimap
    • deque/priority_queue
    • STL
  • 2.2.3 資料結構

    • P1886 滑動視窗 /【模板】單調佇列

      #include <iostream>
      using namespace std;
      
      int n, k, a[1000005];
      int q[1000005], h, t;
      
      void maxx() {
          h = 0; t = -1;
          for (int i = 1; i <= n; ++i) {
              while (h <= t && a[q[t]] <= a[i]) t--;
              q[++t] = i;
              if (q[h] <= i - k) h++;
              if (i >= k) cout << a[q[h]] << " ";
          }
          cout << endl;
      }
      
      void minn() {
          h = 0; t = -1;
          for (int i = 1; i <= n; ++i) {
              while (h <= t && a[q[t]] >= a[i]) t--;
              q[++t] = i;
              if (q[h] <= i - k) h++;
              if (i >= k) cout << a[q[h]] << " ";
          }
          cout << endl;
      }
      
      int main() {
          cin >> n >> k;
          for (int i = 1; i <= n; ++i) cin >> a[i];
          minn();
          maxx();
          return 0;
      }
      
      
    • P3865 【模板】ST 表

      #include <bits/stdc++.h>
      using namespace std;
      const int MAXN = 1e6 + 10;
      int Max[MAXN][21];
      
      int Query(int l, int r) {
          int k = log2(r - l + 1);
          return max(Max[l][k], Max[r - (1 << k) + 1][k]);
      }
      
      int main() {
          ios::sync_with_stdio(false);
          cin.tie(nullptr);
      
          int N, M;
          cin >> N >> M;
          for (int i = 1; i <= N; i++)
              cin >> Max[i][0];
          
          for (int j = 1; j <= 21; j++)
              for (int i = 1; i + (1 << (j - 1)) <= N; i++)
                  Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]);
      
          for (int i = 1; i <= M; i++) {
              int l, r;
              cin >> l >> r;
              cout << Query(l, r) << '\n';
          }
          return 0;
      }
      
      
    • P3367 【模板】並查集

      #include<bits/stdc++.h>
      using namespace std;
      int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
      //f[i]表示i的集合名
      int find(int k){
        //路徑壓縮
          if(f[k]==k)return k;
          return f[k]=find(f[k]);
      }
      int main()
      {
          cin>>n>>m;
          for(i=1;i<=n;i++)
              f[i]=i;//初始化i的老大為自己
          for(i=1;i<=m;i++){
              cin>>p1>>p2>>p3;
              if(p1==1)
                  f[find(p2)]=find(p3);
                  //p3打贏了p2
              else
                  if(find(p2)==find(p3))
                  //是否是一夥的
                      printf("Y\n");
                  else
                      printf("N\n");
          }
          return 0;
      }
      
      
    • P3374【模板】樹狀陣列 1

      #include <bits/stdc++.h> 
      using namespace std;
      int n,m,tree[2000010];
      int lowbit(int k) { return k & -k; }
      void add(int x,int k) {
        while(x<=n) {
          tree[x]+=k;
          x+=lowbit(x);
        }
      }
      int sum(int x) {
        int ans=0;
        while(x!=0) {
          ans+=tree[x];
          x-=lowbit(x);
        }
        return ans;
      }
      int main() {
        cin>>n>>m;
        for(int i=1; i<=n; i++) {
          int a;
          cin>>a;
          add(i,a);
        }
        for(int i=1; i<=m; i++) {
          int a,b,c;
          cin>>a>>b>>c;
          if(a==1)
            add(b,c);
          if(a==2)
            cout<<sum(c)-sum(b-1)<<endl;
        }
      }
      
      
    • P3372 【模板】線段樹 1

      #include <bits/stdc++.h>
      using namespace std;
      #define ll long long
      #define maxn 1000005
      ll a[maxn],w[maxn*4],lzy[maxn*4];
      
      void pushup(int u){w[u]=w[u*2]+w[u*2+1];}   
      
      void build(int u,int L,int R){       //線段樹建立
        if(L==R){w[u]=a[L];return;}
        int M=L+R>>1;
        build(u*2,L,M);
        build(u*2+1,M+1,R);
        pushup(u);
      }
      
      ll query1(int u,int L,int R,int p){   //單點查詢
        if(L==R) return w[u];
        else {
          int M=L+R>>1;
          if(M>=p) return query1(u*2,L,M,p);
          else return query1(u*2+1,M+1,R,p);
        }
      }
      
      void update1(int u,int L,int R,int p,ll x){    //單點修改
        if(L==R) w[u]=x;
        else{
          int M=L+R>>1;
          if(M>=p)  update1(u*2,L,M,p,x);
          else  update1(u*2+1,M+1,R,p,x);
          pushup(u);
        }
      }
      
      bool InRange(int L,int R,int l,int r){return ((l<=L)&&(R<=r));}   //判斷 [L,R] 是否被 [l,r] 包含
      bool OutoRange(int L,int R,int l,int r){ return ((L>r)||(R<l));}  //判斷 [L,R] 是否和 [l,r] 無交
      
      void maketag(int u,int len,int x){
        lzy[u]+=x;
        w[u]+=len*x;
      }
      
      void pushdown(int u,int L,int R){
        int M=L+R>>1;
        maketag(u*2,M-L+1,lzy[u]);
        maketag(u*2+1,R-M,lzy[u]);
        lzy[u]=0;
      }
      ll query(int u,int L,int R,int l,int r){
        if(InRange(L,R,l,r)){return w[u];}
        else if(!OutoRange(L,R,l,r)){
          int M=L+R>>1;
          pushdown(u,L,R);
          return query(u*2,L,M,l,r)+query(u*2+1,M+1,R,l,r);
        }
        else return 0;
      }
      
      
      
      void update(int u,int L,int R,int l,int r,ll x){
        if(InRange(L,R,l,r)){maketag(u,R-L+1,x);}
      
        else if(!OutoRange(L,R,l,r)){
          int M=L+R>>1;
          pushdown(u,L,R);
          update(u*2,L,M,l,r,x);
          update(u*2+1,M+1,R,l,r,x);
          pushup(u);
        }
      }
      int main(){
        ios::sync_with_stdio(false);
        cin.tie(NULL);cout.tie(NULL);
        
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++){cin>>a[i];}
        build(1,1,n);
        for(int i=1;i<=m;i++){
          int op,x,y;
          cin>>op;
          ll k;
          if(op==1){
            cin>>x>>y>>k;
            update(1,1,n,x,y,k);
          }
          else{
            cin>>x>>y;
            cout<<query(1,1,n,x,y)<<endl;
          }
        }
        return 0;
      }
      
    • P8306 【模板】字典樹

      #include<bits/stdc++.h>
      using namespace std;
      int T,q,n,t[3000005][65],cnt[3000005],idx;
      char s[3000005];
      int getnum(char x){
          if(x>='A'&&x<='Z')
              return x-'A';
          else if(x>='a'&&x<='z')
              return x-'a'+26;
          else
              return x-'0'+52;
      } 
      void insert(char str[]){
          int p=0,len=strlen(str);
          for(int i=0;i<len;i++){
              int c=getnum(str[i]);
              if(!t[p][c])
                  t[p][c]=++idx;
              p=t[p][c];
              cnt[p]++;
          }
      }
      int find(char str[]){
          int p=0,len=strlen(str);
          for(int i=0;i<len;i++){
              int c=getnum(str[i]);
              if(!t[p][c])
                  return 0;
              p=t[p][c];
          }
          return cnt[p];
      }
      int main(){
          scanf("%d",&T);
          while(T--){
              for(int i=0;i<=idx;i++)
                  for(int j=0;j<=122;j++)
                      t[i][j]=0;
              for(int i=0;i<=idx;i++)
                  cnt[i]=0;
              idx=0;
              scanf("%d%d",&n,&q);
              for(int i=1;i<=n;i++){
                  scanf("%s",s);
                  insert(s);
              }
              for(int i=1;i<=q;i++){
                  scanf("%s",s);
                  printf("%d\n",find(s));
              }
          }
          return 0;
      }
      
    • P3369 【模板】普通平衡樹

      #include <cstdio>
      #include <cstring>
      #include <iostream>
      #include <algorithm>
      
      using namespace std;
      
      const int N = 100010, INF = 1e8;
      
      int n;
      struct Node
      {
          int l, r;
          int key, val;
          int cnt, size;
      }tr[N];
      
      int root, idx;
      
      void pushup(int p)
      {
          tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
      }
      
      int get_node(int key)
      {
          tr[ ++ idx].key = key;
          tr[idx].val = rand();
          tr[idx].cnt = tr[idx].size = 1;
          return idx;
      }
      
      void zig(int &p)    // 右旋
      {
          int q = tr[p].l;
          tr[p].l = tr[q].r, tr[q].r = p, p = q;
          pushup(tr[p].r), pushup(p);
      }
      
      void zag(int &p)    // 左旋
      {
          int q = tr[p].r;
          tr[p].r = tr[q].l, tr[q].l = p, p = q;
          pushup(tr[p].l), pushup(p);
      }
      
      void build()
      {
          get_node(-INF), get_node(INF);
          root = 1, tr[1].r = 2;
          pushup(root);
      
          if (tr[1].val < tr[2].val) zag(root);
      }
      
      
      void insert(int &p, int key)
      {
          if (!p) p = get_node(key);
          else if (tr[p].key == key) tr[p].cnt ++ ;
          else if (tr[p].key > key)
          {
              insert(tr[p].l, key);
              if (tr[tr[p].l].val > tr[p].val) zig(p);
          }
          else
          {
              insert(tr[p].r, key);
              if (tr[tr[p].r].val > tr[p].val) zag(p);
          }
          pushup(p);
      }
      
      void remove(int &p, int key)
      {
          if (!p) return;
          if (tr[p].key == key)
          {
              if (tr[p].cnt > 1) tr[p].cnt -- ;
              else if (tr[p].l || tr[p].r)
              {
                  if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
                  {
                      zig(p);
                      remove(tr[p].r, key);
                  }
                  else
                  {
                      zag(p);
                      remove(tr[p].l, key);
                  }
              }
              else p = 0;
          }
          else if (tr[p].key > key) remove(tr[p].l, key);
          else remove(tr[p].r, key);
      
          pushup(p);
      }
      
      int get_rank_by_key(int p, int key)    // 透過數值找排名
      {
          if (!p) return 1;   // 本題中不會發生此情況
          if (tr[p].key == key) return tr[tr[p].l].size + 1;
          if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
          return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
      }
      
      int get_key_by_rank(int p, int rank)   // 透過排名找數值
      {
          if (!p) return INF;     // 本題中不會發生此情況
          if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
          if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
          return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
      }
      
      int get_prev(int p, int key)   // 找到嚴格小於key的最大數
      {
          if (!p) return -INF;
          if (tr[p].key >= key) return get_prev(tr[p].l, key);
          return max(tr[p].key, get_prev(tr[p].r, key));
      }
      
      int get_next(int p, int key)    // 找到嚴格大於key的最小數
      {
          if (!p) return INF;
          if (tr[p].key <= key) return get_next(tr[p].r, key);
          return min(tr[p].key, get_next(tr[p].l, key));
      }
      
      int main()
      {
          build();
      
          scanf("%d", &n);
          while (n -- )
          {
              int opt, x;
              scanf("%d%d", &opt, &x);
              if (opt == 1) insert(root, x);
              else if (opt == 2) remove(root, x);
              else if (opt == 3) printf("%d\n", get_rank_by_key(root, x) - 1);
              else if (opt == 4) printf("%d\n", get_key_by_rank(root, x + 1 ));
              else if (opt == 5) printf("%d\n", get_prev(root, x));
              else printf("%d\n", get_next(root, x));
          }
      
          return 0;
      }
      
    • P3370 【模板】字串雜湊

      #include<bits/stdc++.h>
      using namespace std;
      set<string> a;
      int main(){
          string p;
          int n,i;
          cin>>n;
          for(i=0;i<n;i++){
            cin>>p;
            a.insert(p);
        }
        cout<<a.size()<<endl;
        return 0;
      }
      
      
  • 2.2.4 演算法

    • 離散化

      // arr[i] 為初始陣列,下標範圍為 [1, n]
      for (int i = 1; i <= n; ++i)  // step 1
        tmp[i] = arr[i];
      std::sort(tmp + 1, tmp + n + 1);                          // step 2
      int len = std::unique(tmp + 1, tmp + n + 1) - (tmp + 1);  // step 3
      for (int i = 1; i <= n; ++i)                              // step 4
        arr[i] = std::lower_bound(tmp + 1, tmp + len + 1, arr[i]) - tmp;
      
      
      
      
      // std::vector<int> arr;
      std::vector<int> tmp(arr);  // tmp 是 arr 的一個副本
      std::sort(tmp.begin(), tmp.end());
      tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
      for (int i = 0; i < n; ++i)
        arr[i] = std::lower_bound(tmp.begin(), tmp.end(), arr[i]) - tmp.begin();
      
      
      
    • 分治

    • 基數排序

      #include <bits/stdc++.h>
      using namespace std;
      const int N = 1e6 + 5;
      int n, a[N], tot;
      pair<int, int> p[N];
      vector<pair<int, int>> G[N];
      int main(){
          cin >> n;
          for (int i = 1; i <= n; i++){
            cin >> a[i];
              p[i].first = a[i] >> 16,p[i].second = a[i] % 65536;
        }
          for (int i = 1; i <= n; i++)
              G[p[i].second].push_back(p[i]);
          for (int i = 0; i < 65536; i++)
              for (int j = 0; j < G[i].size(); j++)
                  p[++tot] = G[i][j];
          for (int i = 0; i < 65536; i++)
              G[i].clear();
          for (int i = 1; i <= n; i++)
              G[p[i].first].push_back(p[i]);
          tot = 0;
          for (int i = 0; i < 65536; i++)
              for (int j = 0; j < G[i].size(); j++)
                  p[++tot] = G[i][j];
          for (int i = 1; i <= n; i++)cout <<( (p[i].first << 16) | p[i].second )<< " ";
          return 0;
      }
      
    • 歸併排序

      #include <iostream>
      using namespace std;
      const int N=1e6+10;
      int q[N],tmp[N],n;
      void MergeSort(int q[],int l,int r){
        if(l>=r) return ;
        int mid=(r+l)>>1;
        MergeSort(q,l,mid);MergeSort(q,mid+1,r);
        int i=l,j=mid+1,k=0;
        while(i<=mid&&j<=r){
          if(q[i]<=q[j]) tmp[k++]=q[i++];
          else tmp[k++]=q[j++];
        }
        while(i<=mid) tmp[k++]=q[i++];
        while(j<=r) tmp[k++]=q[j++];
        for(int i=l,j=0;i<=r;i++) q[i]=tmp[j++];
      }
      int main(){
        cin>>n;
        for(int i=0;i<n;i++) cin>>q[i];
        MergeSort(q,0,n-1);
        for(int i=0;i<n;i++) cout<<q[i]<<" "; 
        return 0;
      }
      
      
    • P3375 【模板】KMP

      #include<iostream>
      #include<cstring>
      #define MAXN 1000010
      using namespace std;
      int kmp[MAXN];
      int la,lb,j;
      char a[MAXN],b[MAXN];
      int main() {
        cin>>a+1;
        cin>>b+1;
        la=strlen(a+1);
        lb=strlen(b+1);
        for (int i=2; i<=lb; i++) {
          while(j&&b[i]!=b[j+1])
            j=kmp[j];
          if(b[j+1]==b[i])j++;
          kmp[i]=j;
        }
        j=0;
        for(int i=1; i<=la; i++) {
          while(j>0&&b[j+1]!=a[i])
            j=kmp[j];
          if (b[j+1]==a[i])
            j++;
          if (j==lb) {
            cout<<i-lb+1<<endl;
            j=kmp[j];
          }
        }
        for (int i=1; i<=lb; i++)
          cout<<kmp[i]<<" ";
        return 0;
      }
      
    • 搜尋

    • 二分圖的判定:

      int edge[maxn][maxn];//鄰接矩陣儲存
      int color[maxn];//標記頂點顏色
      int n,m;
      bool dfs(int u,int c)
      {
          color[u]=c;//對u點進行染色
          for(int i=1;i<=n;i++)//遍歷與u點相連的點
          {
              if(edge[u][i]==1)//如果i點與u點相連
              {
                  if(color[i]==c) return false;//i點的染色重複,則不是二分圖
                  if(!color[i]&&!dfs(i,-c)) return false;//該點未染色,染上相反的顏色.dfs繼續搜尋
              }
          }
          return true;//所有點染色完成之後,並且相鄰頂點沒有同色,則為二分圖
      }
      
    • P3386 【模板】二分圖最大匹配

      #include <bits/stdc++.h> 
      using namespace std;
      
      const int N=1005;
      struct edge{
        int to,nxt; 
      }e[200010];
      int head[N],cnt,n,m,match[N],T;
      bool vis[N];
      void add(int x,int y){
        e[++cnt]={y,head[x]};
        head[x]=cnt; 
      }
      bool dfs(int x){
        for(int i=head[x];i;i=e[i].nxt){
          int r=e[i].to;
          if(vis[r]) continue;
          vis[r]=true;
          if(match[r]==0||dfs(match[r])){
            match[r]=x;
            return true;
          }
        }
        return false;
      }
      int main(){
        cin>>n>>m>>T;
        while(T--){
          int u,v;
          cin>>u>>v;
          add(u,v+n);
          add(v+n,u); 
        } 
        int ans=0;
        for(int i=1;i<=n;i++){
          memset(vis,false,sizeof(vis));
          if(dfs(i)) ans++; 
        }
        cout<<ans<<endl;
        return 0;
      } 
      
    • P7771 【模板】尤拉路徑

      #include <bits/stdc++.h>
      using namespace std;
      int n,m,u,v,d[100005],du[100005][2];
      stack<int> st;
      vector<int> G[100005];
      void dfs(int now){
        for(int i=d[now];i<G[now].size();i=d[now]){ 
          d[now]=i+1;
          dfs(G[now][i]);
        }
        st.push(now);
      }
      
      int main(){
        cin>>n>>m;
        for(int i=1;i<=m;i++){
          cin>>u>>v;
          G[u].push_back(v);
          du[u][1]++;
          du[v][0]++; 
        }
        for(int i=1;i<=n;i++){
          sort(G[i].begin(),G[i].end());
        }
        int S=1,cnt[2]={0,0};
        bool flag=1;
        for(int i=1;i<=n;i++){
          if(du[i][1]!=du[i][0]){
            flag=0;
            if(du[i][1]-du[i][0]==1) cnt[1]++,S=i;
                  else if(du[i][0]-du[i][1]==1) cnt[0]++;
                  else return puts("No"),0;
          } 
        }
        if(!flag&&!(cnt[0]==cnt[1]&&cnt[0]==1)) return puts("No"),0;
        dfs(S);
        while(!st.empty()) printf("%d ",st.top()),st.pop();
        return 0;
      }
      
      
    • P8436【模板】邊雙連通分量

      #include <iostream>
      #include <cstring>
      #include <vector>
      
      using namespace std;
      
      const int N = 500010, M = 2000010 * 2;
      
      int n, m;
      int h[N], e[M], ne[M], idx;
      int dfn[N], low[N], timestamp;
      int stk[N], top;
      int id[N], dcc_cnt;
      bool is_bridge[M];
      int tot[N];
      vector <int> ans[N];
      
      void add(int a, int b)
      {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx ++ ;
        return;
      }
      
      void tarjan(int u, int from)
      {
        dfn[u] = low[u] = ++ timestamp;
        stk[ ++ top] = u;
        
        for (int i = h[u]; ~i; i = ne[i])
        {
          int j = e[i];
          if (!dfn[j])
          {
            tarjan(j, i);
            low[u] = min(low[u], low[j]);
            if (low[j] > dfn[u])
              is_bridge[i] = is_bridge[i ^ 1] = true;
          }
          else if (i != (1 ^ from))
            low[u] = min(low[u], dfn[j]);
        }
        
        if (dfn[u] == low[u])
        {
          dcc_cnt ++ ;
          int y;
          do
          {
            y = stk[top -- ];
            id[y] = dcc_cnt;
          } while (y != u);
        }
        
        return;
      }
      
      int main()
      {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m;
        memset(h, -1, sizeof h);
        for (int i = 1; i <= m; i ++ )
        {
          int a, b;
          cin >> a >> b;
          add(a, b);
          add(b, a);
        }
        
        for (int i = 1; i <= n; i ++ )
          if (!dfn[i]) tarjan(i, -1);
        
        cout << dcc_cnt << endl;
        
        for (int i = 1; i <= n; i ++ )
        {
          tot[id[i]] ++ ;
          ans[id[i]].push_back(i);
        }
        
        int p = 1;
        while (tot[p])
        {
          cout << tot[p] << ' ';
          for (int i = 0; i < ans[p].size(); i ++ )
            cout << ans[p][i] << ' ';
          p ++ ;
          cout << endl;
        }
        
        return 0;
      }
      
    • P8435【模板】點雙連通分量

      #include <bits/stdc++.h>
      using namespace std;
      const int N = 5e5 + 5, M = 4e6 + 5;
      int cnt = 1, fir[N], nxt[M], to[M];
      int s[M], top, bcc, low[N], dfn[N], idx, n, m;
      vector<int> ans[N];
      inline void tarjan(int u, int fa) {
        int son = 0;
        low[u] = dfn[u] = ++idx;
        s[++top] = u;
        for(int i = fir[u]; i; i = nxt[i]) {
          int v = to[i];
          if(!dfn[v]) {
            son++;
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) {
              bcc++;
              while(s[top + 1] != v) ans[bcc].push_back(s[top--]);//將子樹出棧
              ans[bcc].push_back(u);//把割點/樹根也丟到點雙裡
            }
          } else if(v != fa) low[u] = min(low[u], dfn[v]);
        }
        if(fa == 0 && son == 0) ans[++bcc].push_back(u);//特判獨立點
      }
      inline void add(int u, int v) {
        to[++cnt] = v;
        nxt[cnt] = fir[u];
        fir[u] = cnt;
      }
      int main() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++) {
          int u, v;
          scanf("%d%d", &u, &v);
          add(u, v), add(v, u);
        }
        for(int i = 1; i <= n; i++) {
          if(dfn[i]) continue;
          top = 0;
          tarjan(i, 0);
        }
        printf("%d\n", bcc);
        for(int i = 1; i <= bcc; i++) {
          printf("%d ", ans[i].size());
          for(int j : ans[i]) printf("%d ", j);
          printf("\n");
        }
        return 0;
      }
      
    • DAG拓撲排序:

      #include <bits/stdc++.h>
      using namespace std;
      const int N = 2e5 + 100;
      const int M = 4e5 + 100;
      int head[N], Next[M], ver[M], tot;
      int deg[N];
      void add(int x, int y) {
          ver[++tot] = y;
          Next[tot] = head[x];
          head[x] = tot;
      }
      queue<int>qc;
      int topsort(int n) {
          int cnt = 0;
          while (qc.size())
              qc.pop();
          for (int i = 1; i <= n; i++) {
              if (deg[i] == 0) {
                  qc.push(i);
              }
          }
          while (qc.size()) {
              int x = qc.front();
              cnt++;
              qc.pop();
              for (int i = head[x]; i; i = Next[i]) {
                  int y = ver[i];
                  if (--deg[y] == 0)
                      qc.push(y);
              }
          }
          return cnt == n;
      }
      int main() {
          int n, m, x, y;
          cin >> n >> m;
          for (int i = 1; i <= m; i++) {
              cin >> x >> y;
              add(x, y);
              deg[y]++;
          }
          if (topsort(n))
              cout << "Yes" << endl;
          else 
              cout << "No" << endl;
      }
      
    • Dijkstra \(O((n+m)\times log(n+m))\)

      #include <bits/stdc++.h>
      #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
      #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
      #define ROF(i,a,b) for(int i=(a);i>=(b);--i)
      #define U unsigned
      #define LL long long
      using namespace std;
      template<class T>
      inline void read(T &a){ register U LL x=0,t=1; register char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') t=-1; ch=getchar();} while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } a=x*t;}
      inline void print(LL x){if(x<0)putchar('-'),x=-x;if(x>9) print(x/10);putchar(x%10+'0');}
      int n,m,s;
      vector<pair<int,int> > v[200005];
      int dis[200005];
      struct node{
        int d,id;
        bool friend operator < (node xx,node yy){
          return xx.d>yy.d;
        }
      };
      priority_queue<node>Q;    
      void dij(){
        for(int i=1;i<=n;i++) dis[i]=2e9;
        dis[s]=0;
        Q.push({0,s});
        while(!Q.empty()){
          int d=Q.top().d; 
          int id=Q.top().id;
          Q.pop();
          if(d>dis[id]) continue;
          for(int i=0;i<v[id].size();i++){
            int to=v[id][i].first;
            if(dis[to]>dis[id]+v[id][i].second){
              dis[to]=dis[id]+v[id][i].second;
              Q.push({dis[to],to});
            }
          }
        }
      }
      void sovle(){
        cin>>n>>m>>s;
        for(int i=1;i<=m;i++){
          int x,y,c;
          cin>>x>>y>>c;
          v[x].push_back({y,c});
        }
        dij();
        for(int i=1;i<=n;i++){
          cout<<dis[i]<<" ";
        } 
      }
      signed main(){
          sovle();
        return 0;
      }
      
    • SPFA:

      #include<bits/stdc++.h>
      #define inf 1000000000
      #define N 200020
      using namespace std;
      queue<int>Q;
      int dis[N];
      bool vis[N];
      int n, m, s;
      int head[N], pos;
      struct edge { int to, next, c; }e[N];
      void add(int a, int b, int c) {
          pos++; e[pos].to = b, e[pos].c = c, e[pos].next = head[a], head[a] = pos;
      }
      void spfa() {
          for (int i = 1; i <= n; i++)
              dis[i] = inf, vis[i] = 0;
          dis[s] = 0, vis[s] = 1, Q.push(s);
          while (!Q.empty())
          {
              int u = Q.front(); Q.pop(); vis[u] = 0;
              for (int i = head[u]; i; i = e[i].next)
              {
                  int v = e[i].to;
                  if (dis[v] > dis[u] + e[i].c) {
                      dis[v] = dis[u] + e[i].c;
                      if (!vis[v])vis[v] = 1, Q.push(v);
                  }
              }
          }
      }
      int main()
      {
          cin >> n >> m >> s;
          for (int i = 1; i <= m; i++) {
              int x, y, c;
              cin >> x >> y >> c;
              add(x, y, c);
              add(y, x, c);
          }
          spfa();
      }
      
    • Floyd:

      for (k = 1; k <= n; k++)
        for (x = 1; x <= n; x++)
          for (y = 1; y <= n; y++)
            f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
      
    • Bellman-Ford:

      int dis[maxv][maxv];        //dis[k][v];表示選取前k個時到達i的最短距離
      struct Edge
      {
          int u, v, w;
      }edge[maxv];
      int n, m;
      
      void Bellman_Ford(int s)
      {
          memset(dis, INF, sizeof(dis));
          for (int i = 1; i <= n; i++)  dis[i][s] = 0;
          for (int k = 1; k <= n - 1; k++)            
              for (int i = 0; i < m; i++)                
              {
                  int u = edge[i].u, v = edge[i].v, w = edge[i].w;
                  dis[k][v] = min(dis[k][v], dis[k - 1][u] + w);
              }
      }
      
    • Kosaraju:

      #include<bits/stdc++.h>
      #define N 200020
      using namespace std;
      int n, m;
      int head[N], pos;
      struct edge { to, next, c; }e[N << 1];
      void add(int a, int b, int c)
      {
          pos++; e[pos].to = b, e[pos].next = head[a], head[a] = pos; e[pos].c = c;
      }
      int tot, scc;
      int st[N], top;
      bool vis[N];
      void dfs1(int u)
      {
          vis[u] = 1;
          for (int i = head[u]; i; i = e[i].next)
          {
              int v = e[i].to;
              if (vis[v] || e[i].c == 0)continue;
              dfs1(v);
          }
          st[++top] = u;
      }
      int bel[N];
      void dfs2(int u, int bl)
      {
          bel[u] = bl, vis[u] = 1;
          for (int i = head[u]; i; i = e[i].next)
          {
              int v = e[i].to;
              if (vis[v] || e[i].c)continue;
              dfs2(v, bl);
          }
      }
      int main()
      {
          cin >> n >> m;
          for (int i = 1; i <= m; i++)
          {
              int x, y;
              cin >> x >> y;
              add(x, y, 1), add(y, x, 0);
          }
          for (int i = 1; i <= n; i++)
              if (!vis[i])dfs1(i);
          for (int i = 1; i <= n; i++)
              vis[i] = 0;
          for (int i = top; i; i--)
              if (!vis[st[i]]) {
                  ++scc;
                  dfs2(st[i], scc);
              }
          cout << scc << endl; //scc是強連通分量的數量
      }
      
    • Tarjan(強連通分量):

      #include<bits/stdc++.h>
      using namespace std;
      
      int n,m,cnt,cntb;
      vector<int> edge[10001];
      vector<int> belong[10001];
      bool instack[10001];
      int dfn[10001];
      int low[10001];
      stack<int> s;
      void Tarjan(int u){
        ++cnt;
        dfn[u]=low[u]=cnt;
        s.push(u);
        instack[u]=true; 
        for(int i=0;i<edge[u].size();++i){
          int v=edge[u][i];
          if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
          }
          else if(instack[v])
            low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u]){
          ++cntb;
          int node;
          do{
            node=s.top();
            s.pop();
            instack[node]=false;
            belong[cntb].push_back(node);
          }while(node!=u);
        }
      }
      int main(){
        cin>>n>>m;
        for(int i=1;i<=m;++i){
          int u,v;
          cin>>u>>v;
          edge[u].push_back(v);
        }
        Tarjan(1);
        return 0;
      } 
      
    • Tarjan:割點

      #include <bits/stdc++.h>
      using namespace std;
      constexpr int N = 1e5 + 5;
      int n, m, num,root;
      int dn, dfn[N], low[N], cnt, buc[N],sum; 
      vector<int> e[N];
      void Tarjan(int x){
        dfn[x]=low[x]=++num;
        int flag=0;
        for(int i=0;i<e[x].size();i++){
          int to=e[x][i];
          if(!dfn[to]){
            Tarjan(to);
            low[x]=min(low[x],low[to]);
            if(low[to]>=dfn[x]){
              flag++;
              if(x!=root||(flag>=2)){
                buc[x]=1;
              }
            }
          }
          else low[x]=min(low[x],dfn[to]);
        }
      }
      int main() {
        cin >> n >> m;
        for(int i = 1; i <= m; i++) {
          int u, v;
          cin >> u >> v;
          e[u].push_back(v);
          e[v].push_back(u);
        }
        for(int i=1;i<=n;i++){
          if(!dfn[i]){
            root=i;
            Tarjan(i);
          } 
        }
        for(int i=1;i<=n;i++){
          if(buc[i]) sum++; 
        } 
        cout<<sum<<endl;
        for(int i=1;i<=n;i++){
          if(buc[i]) cout<<i<<" ";
        } 
        return 0;
      }
      
    • Tarjan: 割邊

      #include<iostream>
      #include<cmath>
      using namespace std;
      int n, m, root, a, b, total;
      int e[101][101], dfn[101], low[101], flag[101], head[101];
      
      struct node{
          int to;
          int next;
      }edge[10010];
      
      int cnt = 1;
      void add(int u, int v) {
          edge[cnt].to = v;
          edge[cnt].next = head[u];
          head[u] = cnt++;
      }
      
      void tarjan(int u, int father) {
          int child = 0;
          dfn[u] = low[u] = ++total;
          for (int i = head[u]; i != 0; i = edge[i].next) {
              int v = edge[i].to;
              if (!dfn[v]) {
                  child++;
                  tarjan(v, u);
                  low[u] = min(low[u], low[v]);
                  if  (low[v] > dfn[u]) {
                      cout << u << "->" << v << endl;
                  }
              } else if (v != father) {
                  low[u] = min(low[u], dfn[v]);
              }
          }
      }
      
      
      int main() {
          cin >> n >> m;
          for (int i = 0; i < m; i++) {
              cin >> a >> b;
              add(a, b);
              add(b, a);
          }
          root = 1;
          tarjan(1, root);
          for (int i = 1; i <= n; i++) {
              if(flag[i]) {
                  cout << i << " ";
              }
          }
          return 0;
      }
      
    • 差分約束:

      #include <cstring>
      #include <iostream>
      #include <queue>
      #include <algorithm>
      using namespace std;
      struct edge {
        int v,w,next;
      } e[10005];
      int head[5005],tot[5005],dis[5005],vis[5005],cnt,n,m;
      void addedge(int u,int v,int w) {
        e[++cnt].v=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
      }
      bool spfa(int s) {
        queue<int> q;
        memset(dis,63,sizeof(dis));
        dis[s]=0,vis[s]=1;
        q.push(s);
        while(!q.empty()) {
          int u=q.front();
          q.pop();
          vis[u]=0;
          for(int i=head[u]; i; i=e[i].next) {
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].w) {
              dis[v]=dis[u]+e[i].w;
              if(!vis[v]) {
                vis[v]=1,tot[v]++;
                if(tot[v]==n+1)return false; // 注意新增了一個超級源點
                q.push(v);
              }
            }
          }
        }
        return true;
      }
      int main() {
        cin>>n>>m;
        for(int i=1; i<=n; i++)
          addedge(0,i,0);
        for(int i=1; i<=m; i++) {
          int v,u,w;
          cin>>v>>u>>w;
          addedge(u,v,w);
        }
        if(!spfa(0))cout<<"NO"<<endl;
        else
          for(int i=1; i<=n; i++)
            cout<<dis[i]<<' ';
        return 0;
      }
      
      • 嚴格單元次短路:

      #include <bits/stdc++.h>
      using namespace std;
      
      const int MAXN = 200010;
      struct edge{
        int to,nxt,v;
      }e[MAXN];
      int h[MAXN],cnt,dis[2][MAXN],n,m;
      void add(int u,int v,int w){
        e[++cnt].nxt=h[u];
          e[cnt].to=v; 
          e[cnt].v=w;
        h[u]=cnt;
      }
      struct node{
        int pos,dis;
        friend bool operator<(node a,node b){
          return a.dis>b.dis;
        }
      }tmp;
      
      priority_queue<node> q;
      void dij(){
        for(int i=1;i<=n;i++){
          dis[0][i]=dis[1][i]=2147483647;
        }
        dis[0][1]=0;
        tmp.dis=0,tmp.pos=1;
        q.push(tmp);
        while(!q.empty()){
          tmp=q.top();
          q.pop();
          int u=tmp.pos,d=tmp.dis;
          if(d>dis[1][u]) continue;
          for(int i=h[u];i;i=e[i].nxt){
            int v=e[i].to;
            int w=e[i].v;
            if(dis[0][v]>d+w){
              dis[1][v]=dis[0][v];
              tmp.dis=dis[0][v]=d+w;
              tmp.pos=v;
              q.push(tmp);
            }
            if(dis[1][v]>d+w&&dis[0][v]<d+w){
              tmp.dis=dis[1][v]=d+w;
              tmp.pos=v;
              q.push(tmp);
            }
          }
        }
      }
      int main(){
        cin>>n>>m;
        for(int i=1;i<=m;i++){
          int a,b,c;
          cin>>a>>b>>c;
          add(a,b,c);
          add(b,a,c);
        }
        
        dij();
        cout<<dis[1][n];
      }
      
    • 嚴格次小生成樹:

      #include <bits/stdc++.h>
      using namespace std;
      typedef long long ll;
      const int N = 100010;
      const ll INF = 1e18;
      int n, m,d[N];
      bool vis[N];
      ll f[N][25],g1[N][25],g2[N][25],mst, ans = INF;
      struct Node {
        ll to,cost;
      };
      vector<Node> v[N];
      
      void dfs(const int x) {
        vis[x] = true;
        for (int i = 0; i < v[x].size(); i++) {
          int y = v[x][i].to;
          if (vis[y]) continue;
          d[y] = d[x] + 1;
          f[y][0] = x;
          g1[y][0] = v[x][i].cost;
          g2[y][0] = -INF;
          dfs(y);
        }
      }
      
      inline void prework() {
        for (int i = 1; i <= 20; i++)
          for (int j = 1; j <= n; j++) {
            f[j][i] = f[f[j][i - 1]][i - 1];
            g1[j][i] = max(g1[j][i - 1], g1[f[j][i - 1]][i - 1]);
            g2[j][i] = max(g2[j][i - 1], g2[f[j][i - 1]][i - 1]);
            if (g1[j][i - 1] > g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[f[j][i - 1]][i - 1]);
            else if (g1[j][i - 1] < g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[j][i - 1]);
          }
      }
      
      inline void LCA(int x, int y, const ll w) {
        ll zui = -INF, ci = -INF;
        if (d[x] > d[y]) swap(x, y);
        for (int i = 20; i >= 0; i--)
          if (d[f[y][i]] >= d[x]) {
            zui = max(zui, g1[y][i]);
            ci = max(ci, g2[y][i]);
            y = f[y][i];
          }
        if (x == y) {
          if (zui != w) ans = min(ans, mst - zui + w);
          else if (ci != w && ci > 0) ans = min(ans, mst - ci + w);
          return;
        }
        for (int i = 20; i >= 0; i--)
          if (f[x][i] != f[y][i]) {
            zui = max(zui, max(g1[x][i], g1[y][i]));
            ci = max(ci, max(g2[x][i], g2[y][i]));
            x = f[x][i];
            y = f[y][i];
          }
        zui = max(zui, max(g1[x][0], g1[y][0]));
        if (g1[x][0] != zui) ci = max(ci, g1[x][0]);
        if (g2[y][0] != zui) ci = max(ci, g2[y][0]);
        if (zui != w) ans = min(ans, mst - zui + w);
        else if (ci != w && ci > 0) ans = min(ans, mst - ci + w);
      }
      
      struct Edge {
        int from, to;
        ll cost;
        bool is_tree;
      } edge[N * 3];
      
      bool operator < (const Edge x, const Edge y) {
        return x.cost < y.cost;
      }
      
      int fa[N];
      
      inline int find(const int x) {
        if (fa[x] == x) return x;
        else return fa[x] = find(fa[x]);
      }
      
      inline void Kruskal() {
        sort(edge, edge + m);
        for (int i = 1; i <= n; i++)
          fa[i] = i;
        for (int i = 0; i < m; i++) {
          int x = edge[i].from;
          int y = edge[i].to;
          ll z = edge[i].cost;
          int a = find(x), b = find(y);
          if (a == b) continue;
          fa[find(x)] = y;
          mst += z;
          edge[i].is_tree = true;
          v[x].push_back((Node) {
            y, z
          });
          v[y].push_back((Node) {
            x, z
          });
        }
      }
      
      int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cin >> n >> m;
        for (int i = 0, x, y; i < m; i++) {
          ll z;
          cin >> x >> y >> z;
          if (x == y) continue;
          edge[i].from = x;
          edge[i].to = y;
          edge[i].cost = z;
        }
        Kruskal();
        d[1] = 1;
        dfs(1);
        prework();
        for (int i = 0; i < m; i++)
          if (!edge[i].is_tree)
            LCA(edge[i].from, edge[i].to, edge[i].cost);
        cout << ans << "\n";
        return 0;
      }
      
    • Kruskal:

      #include <bits/stdc++.h>
      using namespace std;
      const int N=2000200;
      int n,m,f[N];
      long long ans=0;
      struct edge{
        int u,v,c;
      }e[N];
      bool operator<(edge x,edge y){
        return x.c<y.c;
      }
      int find(int x){
        return x==f[x]?x:f[x]=find(f[x]);
      }
      void sovle(){
        cin>>n>>m;
        for(int i=1;i<=m;i++){
          cin>>e[i].u>>e[i].v>>e[i].c;
        }
        sort(e+1,e+m+1);
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=m;i++){
          int fu=find(e[i].u),fv=find(e[i].v);
          if(fu==fv) continue;
          f[fu]=fv;
          ans+=e[i].c;
        }
        cout<<ans<<endl;
      }
      
      signed main(){
          sovle();
        return 0;
      }
      
    • Prim:

      #include <bits/stdc++.h>
      using namespace std;
      
      const int maxn = 5010;
      const int inf = 0x3f3f3f3f;
      int g[maxn][maxn];
      int dis[maxn];
      bool vis[maxn];
      int n, m, u, v, w,cnt;
      
      int prime() {
          int tot = 0;
          memset(dis, inf, sizeof(dis));
          memset(vis, false, sizeof(vis));
          dis[1] = 0;
          for (int i = 1; i <= n; ++i) {
              int minv = inf, u = -1;
              for (int j = 1; j <= n; ++j) {
                  if (!vis[j] && minv > dis[j]) {
                      minv = dis[j];
                      u = j;
                  }
              }
              if (u == -1) return -1;
              vis[u] = true;
              tot += minv;
              cnt++;
              for (int j = 1; j <= n; ++j) {
                  if (!vis[j] && dis[j] > g[u][j]) {
                      dis[j] = g[u][j];
                  }
              }
          }
          return tot;
      }
      
      int main() {
          memset(g, inf, sizeof(g));
          cin >> n >> m;
          for (int i = 0; i < m; ++i) {
              cin >> u >> v >> w;
              g[u][v] = g[v][u] = min(g[u][v], w);
          }
          int tmp=prime();
          if(cnt>=n-1)
              cout << tmp << endl;
          return 0;
      }
      
    • 樹的直徑:

      #include <bits/stdc++.h>
      #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
      using namespace std;
      int dp[100010]={0},ans;
      vector<int> G[100010];
      void dfs(int rt,int fa){
        dp[rt]=0;
        int mx=0,mn=0;
        for(int i=0;i<G[rt].size();i++){
          int to=G[rt][i];
          if(to==fa) continue;
          dfs(to,rt);
          if(dp[to]>mx){
            mn=mx;
            mx=dp[to];
          }
          else if(dp[to]>mn) mn=dp[to];
        }
        dp[rt]=mx+1;
        ans=max(ans,mx+mn);
        
      }
      int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n-1;i++){
          int u,v;
          cin>>u>>v;
          G[u].push_back(v);
          G[v].push_back(u);
        }
        dfs(1,-1);
        cout<<ans<<endl;
        return 0;
      
      }
      
    • 樹的重心:

      #include <bits/stdc++.h>
      using namespace std;
      
      const int MAXN=1000010;
      const int inf=0x7f7f7f7f;
      
      int f[MAXN],size[MAXN],head[MAXN],dep[MAXN];
      int n,center,sum;
      vector<int> G[MAXN];
      queue<int> q;
      
      void dfs(int u,int fa){
        size[u]=1;
        f[u]=0;
        for(int i=0;i<G[u].size();i++){
          int v=G[u][i];
          if(v==fa) continue;
          dfs(v,u);
          size[u]+=size[v];
          f[u]=max(f[u],size[v]);
        }
        f[u]=max(f[u],n-size[u]);
        if(f[u]<f[center]||(f[u]==f[center]&&u<center)){
          center=u;
        }
      }
      
      void bfs(){
        q.push(center);
        while(!q.empty()){
          int u=q.front();
          q.pop();
          for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            if(dep[v]||v==center) continue;
            dep[v]=dep[u]+1;
            sum+=dep[v];
            q.push(v);
          }
        }
      }
      int main(){
        cin>>n;
        for(int i=1;i<=n-1;i++){
          int u,v;
          cin>>u>>v;
          G[u].push_back(v);
          G[v].push_back(u);
        }
        center=0;
        f[0]=inf;
        dfs(1,0);
        bfs();
        cout<<center<<" "<<sum<<endl; 
        return 0;
      }
      
    • 樹的中心:

      #include<bits/stdc++.h>
      using namespace std;
      const int N = 10010, M = N * 2, INF = 0x3f3f3f3f;
      int n;
      int h[N], e[M], w[M], ne[M], idx;
      int d1[N], d2[N], p1[N], up[N];
      bool is_leaf[N];
      void add(int a, int b, int c) {
          e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
      }
      int dfs_d(int u, int father) {
          d1[u] = d2[u] = -INF;
          for (int i = h[u]; i != -1; i = ne[i]) {
              int j = e[i];
              if (j == father) continue;
              int d = dfs_d(j, u) + w[i];
              if (d >= d1[u]) {
                  d2[u] = d1[u], d1[u] = d;
                  p1[u] = j;
              } else if (d > d2[u]) d2[u] = d;
          }
          if (d1[u] == -INF) {
              d1[u] = d2[u] = 0;
              is_leaf[u] = true;
          }
          return d1[u];
      }
      void dfs_u(int u, int father) {
          for (int i = h[u]; i != -1; i = ne[i]) {
              int j = e[i];
              if (j == father) continue;
      
              if (p1[u] == j) up[j] = max(up[u], d2[u]) + w[i];
              else up[j] = max(up[u], d1[u]) + w[i];
      
              dfs_u(j, u);
          }
      }
      int main() {
          cin >> n;
          memset(h, -1, sizeof h);
          for (int i = 0; i < n - 1; i ++ ) {
              int a, b, c;
              cin >> a >> b >> c;
              add(a, b, c), add(b, a, c);
          }
          dfs_d(1, -1);
          dfs_u(1, -1);
          int res = d1[1];
          for (int i = 2; i <= n; i ++ )
              if (is_leaf[i]) res = min(res, up[i]);
              else res = min(res, max(d1[i], up[i]));
          printf("%d\n", res);
          return 0;
      }
      
    • LCA:

      #include <bits/stdc++.h>
      #define ll long long
      #define ull unsigned long long
      using namespace std;
      typedef pair<int,int> PII;
      const int inf = 0x3f3f3f3f;
      const int mod = 998244353;
      const int N = 5e5+10;
      const int base = 233;
      int n,m,s;
      int pre[N][35],dep[N];
      vector<int>v[N];
      void init(int l,int ff){
          pre[l][0] = ff;
          dep[l] = dep[ff] + 1;
          for(int r:v[l]){
              if(r==ff)continue;
              init(r,l);
          }
      }
      void init(){
          init(s,0);
          for(int k=1;k<=25;k++){
              for(int i=1;i<=n;i++){
                  pre[i][k] = pre[ pre[i][k-1] ][k-1];
              }
          }
      }
      int LCA(int x,int y){
          //1.同深度
          if(dep[x]<dep[y])swap(x,y);
          for(int k=25;k>=0;k--){
              int fa = pre[x][k];
              if(dep[fa]>=dep[y])x = fa;
          }
          if(x==y)return x;
          //2.一起向上跳 
          for(int k=25;k>=0;k--){
              int fax = pre[x][k];
              int fay = pre[y][k];
              if(fax != fay){
                  x = fax;
                  y = fay;
              }
          }
          return pre[y][0];
      }
      inline void slove(){
          scanf("%d%d%d",&n,&m,&s);
          for(int i=1;i<n;i++){
              int x,y;scanf("%d%d",&x,&y);
              v[x].push_back(y);
              v[y].push_back(x);
          }
          init();
          while (m--){
              int x,y;scanf("%d%d",&x,&y);
              printf("%d\n",LCA(x,y));
          }
      }
      int main(){
          int TT=1;
          while(TT--) slove();
          return 0;
      }
      
    • 樹上差分

      #include <bits/stdc++.h>
      #define ll long long
      #define ull unsigned long long
      using namespace std;
      typedef pair<int,int> PII;
      const int inf = 0x3f3f3f3f;
      const int mod = 998244353;
      const int N = 5e5+10;
      const int base = 233;
      int n,m,s;
      int pre[N][35],dep[N],val[N];
      vector<int>v[N];
      void init(int l,int ff){
          dep[l] = dep[ff] + 1;
          pre[l][0] = ff;
          for(int r:v[l]){
              if(r==ff)continue;
              init(r,l);
          }
      }
      void init(){
          init(1,0);
          for(int k=1;k<=25;k++){
              for(int i=1;i<=n;i++){
                  pre[i][k] = pre[pre[i][k-1]][k-1];
              }
          }
      }
      int lca(int x,int y){
          if(dep[x]<dep[y])swap(x,y);
          for(int k=25;k>=0;k--){
              if(dep[pre[x][k]]>=dep[y]) x = pre[x][k];
          }
          if(x==y)return x;
          for(int k=25;k>=0;k--){
              if(pre[x][k] != pre[y][k])x = pre[x][k],y = pre[y][k];
          }
          return pre[x][0];
      }
      int ans = 0;
      void dfs(int l,int ff){
          for(int r:v[l]){
              if(r==ff)continue;
              dfs(r,l);
              val[l] += val[r];
          }
          ans = max(ans,val[l]);
      }
      inline void slove(){
          scanf("%d%d",&n,&m);
          for(int i=1;i<n;i++){
              int x,y;scanf("%d%d",&x,&y);
              v[x].push_back(y);
              v[y].push_back(x);
          }
          init();
          while (m--){
              int x,y;scanf("%d%d",&x,&y);
              val[x]++;
              val[y]++;
              int LCA = lca(x,y);
              val[LCA]--;
              val[pre[LCA][0]]--;
          }
          dfs(1,0);
          printf("%d\n",ans);
      }
      int main(){
          int TT=1;
          while(TT--)slove();
          return 0;
      }
      
      
    • 最長上升子序列:

      • \(O(n^2)\)
      #include <bits/stdc++.h>
      using namespace std;
      int main(){
        int n,a[100005],dp[100005],MAX;
        cin>>n; 
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++){
          MAX=0;
          for(int j=1;j<=i-1;j++)
            if(a[i]>a[j]) MAX=max(MAX,dp[j]);
          dp[i]=MAX+1;
        }
        MAX=0;
        for(int i=1;i<=n;i++) MAX=max(MAX,dp[i]);
        cout<<MAX<<endl;
      }
      
      • \(O(nlogn)\)
      #include <bits/stdc++.h>
      using namespace std;
      int a[10005],f[10005];
      int cnt;
      int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++){
              int pos=lower_bound(f+1,f+cnt+1,a[i])-f;
              if(pos==cnt+1) f[++cnt]=a[i];
              else f[pos]=a[i];
        }
        cout<<cnt<<endl;
          for(int i=1;i<=cnt;i++) cout<<f[i]<<" ";
        return 0;
      }
      
      
    • 最長公共子序列:

      • \(O(n^2)\)
      #include<iostream>
      using namespace std;
      int dp[1001][1001],a1[2001],a2[2001],n,m;
      int main(){
          cin>>n;
          for(int i=1;i<=n;i++)scanf("%d",&a1[i]);
          for(int i=1;i<=n;i++)scanf("%d",&a2[i]);
          for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
              dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
              if(a1[i]==a2[j])
              dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
            }
          cout<<dp[n][n];
      }
      
      • \(O(nlogn)\)
      #include<bits/stdc++.h>
      using namespace std;
      int a[100001],b[100001],mp[100001],f[100001];
      int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
                  cin>>a[i];
                  mp[a[i]]=i;
          }
        for(int i=1;i<=n;i++){
                cin>>b[i];
                f[i]=0x7fffffff;
          }
        int len=0;
        f[0]=0;
        for(int i=1;i<=n;i++){
          int l=0,r=len,mid;
          if(mp[b[i]]>f[len])f[++len]=mp[b[i]];
          else
          {
              int pos=lower_bound(f,f+len,mp[b[i]])-f;
                  f[pos]=min(mp[b[i]],f[pos]);
            }
          }
          cout<<len;
          return 0;
      }
      
      
    • 01揹包:

      • 空間複雜度 \(O(nV)\)
      #include <bits/stdc++.h>
      using namespace std;
      int f[1005][1005];    //f[i][j]表示選前i個物品重量不超過j的最大值
      int main(){
        int n,V;
        cin>>n>>V;
        for(int i=1;i<=n;i++){
          int v,w;
          cin>>v>>w;
          for(int j=1;j<=V;j++){
            if(v<=j) 
              f[i][j]=max(f[i-1][j],f[i-1][j-v]+w); 
            else
              f[i][j]=f[i-1][j];
          } 
        }
        cout<<f[n][V]<<endl;
        return 0;
      } 
      
      • 空間複雜度 \(O(V)\)
      #include <bits/stdc++.h>
      using namespace std;
      int f[1005];
      int main(){
        int n,V;
        cin>>n>>V;
        for(int i=1;i<=n;i++){
          int v,w;
          cin>>v>>w;
          for(int j=V;j>=v;j--) f[j]=max(f[j],f[j-v]+w);
        }
        cout<<f[V]<<endl;
        return 0;
      } 
      
      • 二維費用01揹包
      #include <iostream>
      using namespace std;
      const int N = 110;
      int n, V, M,f[N][N];
      int main(){
          cin >> n >> V >> M;
          for (int i = 0; i < n; i ++ ){
              int v, m, w;
              cin >> v >> m >> w;
              for (int j = V; j >= v; j -- )
                  for (int k = M; k >= m; k -- )
                      f[j][k] = max(f[j][k], f[j - v][k - m] + w);
          }
          cout << f[V][M] << endl;
          return 0;
      }
      
      • 01揹包求具體方案:
      #include <iostream>
      using namespace std;
      const int N = 1010;
      int n, m,v[N], w[N],f[N][N];
      int main(){
          cin >> n >> m;
          for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
          for (int i = n; i >= 1; i -- )   //要求輸出字典序最小所以考慮倒著做,即 f[1][m] 為最大價值
              for (int j = 0; j <= m; j ++ ){
                  f[i][j] = f[i + 1][j];
                  if (j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
              }
          int j = m;
          for (int i = 1; i <= n; i ++ )
              if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]){
                  cout << i << ' ';
                  j -= v[i];
              }
          return 0;
      }
      
    • 完全揹包:

      • 空間複雜度 \(O(nV)\)
      #include <bits/stdc++.h>
      using namespace std;
      int f[1005][1005];    
      int main(){
        int n,V;
        cin>>n>>V;
        for(int i=1;i<=n;i++){
          int v,w;
          cin>>v>>w;
          for(int j=0;j<=V;j++){
              f[i][j]=f[i-1][j];
              if(v<=j) 
                  f[i][j]=max(f[i][j],f[i][j-v]+w); 
          } 
        }
        cout<<f[n][V]<<endl;
        return 0;
      } 
      
      • 空間複雜度 \(O(V)\)
      #include <bits/stdc++.h>
      using namespace std;
      int f[1005];
      int main(){
        int n,V;
        cin>>n>>V;
        for(int i=1;i<=n;i++){
          int v,w;
          cin>>v>>w;
          for(int j=v;j<=V;j++) f[j]=max(f[j],f[j-v]+w);
        }
        cout<<f[V]<<endl;
        return 0;
      } 
      
    • 多重揹包:

      • 時間複雜度 \(O(nVs)\) (樸素版)
      #include <bits/stdc++.h>
      using namespace std;
      const int N = 110;
      int v[N], w[N], s[N],f[N][N],n, m;
      int main(){
          cin >> n >> m;
          for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i] >> s[i];
          for(int i = 1; i <= n; i ++){//列舉揹包
              for(int j = 1; j <= m; j ++){//列舉體積
                  for(int k = 0; k <= s[i]; k ++)
                      if(j >=  k * v[i])
                          f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
              }
          }
          cout << f[n][m] << endl;
          return 0;
      }
      
      • 時間複雜度 \(O(n \ logs V)\) (二進位制分解)
      #include<bits/stdc++.h>
      using namespace std;
      const int N = 12010, M = 2010;
      int n, m,v[N], w[N],f[M];
      int main(){
          cin >> n >> m;
          int cnt = 0; //分組的組別
          for(int i = 1;i <= n;i ++){
              int a,b,s;
              cin >> a >> b >> s;
              int k = 1; // 組別裡面的個數
              while(k<=s){
                  cnt ++ ; //組別先增加
                  v[cnt] = a * k ; //整體體積
                  w[cnt] = b * k; // 整體價值
                  s -= k; // s要減小
                  k *= 2; // 組別裡的個數增加
              }
              //剩餘的一組
              if(s>0){
                  cnt ++ ;
                  v[cnt] = a*s; 
                  w[cnt] = b*s;
              }
          }
          n = cnt ; //列舉次數正式由個數變成組別數
          //01揹包一維最佳化
          for(int i = 1;i <= n ;i ++)
              for(int j = m ;j >= v[i];j --)
                  f[j] = max(f[j],f[j-v[i]] + w[i]);
          cout << f[m] << endl;
          return 0;
      }
      
      • 時間複雜度 \(O(nV)\) (單調佇列最佳化)
      #include<bits/stdc++.h>
      using namespace std;
      const int N = 20010;
      int n, m,f[N], g[N], q[N];
      int main(){
          cin >> n >> m;
          for (int i = 0; i < n; i ++ ){
              int v, w, s;
              cin >> v >> w >> s;
              memcpy(g, f, sizeof f);   //滾動陣列
              for (int j = 0; j < v; j ++ ){
                  int hh = 0, tt = -1;
                  for (int k = j; k <= m; k += v){    //單調佇列求區間最大值
                      if (hh <= tt && q[hh] < k - s * v) hh ++ ;
                      while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
                      q[ ++ tt] = k;
                      f[k] = g[q[hh]] + (k - q[hh]) / v * w;
                  }
              }
          }
          cout << f[m] << endl;
          return 0;
      }
      
      
    • 分組揹包:

      • \(O(nms)\)
      #include<bits/stdc++.h>
      using namespace std;
      const int N=110;
      int f[N],v[N][N],w[N][N],s[N],n,m,k;
      int main(){
          cin>>n>>m;
          for(int i=0;i<n;i++){
              cin>>s[i];
              for(int j=0;j<s[i];j++)
                  cin>>v[i][j]>>w[i][j];
          }
          for(int i=0;i<n;i++){   //列舉組
              for(int j=m;j>=0;j--){
                  for(int k=0;k<s[i];k++){    //for(int k=s[i];k>=0;k--)也可以
                      if(j>=v[i][k])     f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);  
                  }
              }
          }
          cout<<f[m]<<endl;
      }
      
      • 分組揹包求具體方案
      #include<bits/stdc++.h>
      using namespace std;
      const int N=1010;
      int f[N][N],g[N][N],res[N];   //g[i][j] 為第i組的體積為j的價值
      int main(){
          int n,m;
          cin>>n>>m;
          for(int i=1;i<=n;i++)
              for(int j=1;j<=m;j++)
                  cin>>g[i][j];
          for(int i=n;i;i--)    //要求輸出字典序最小所以考慮倒著做,即 f[1][m] 為最大價值
              for(int j=0;j<=m;j++)
                  for(int k=0;k<=j;k++)
                      f[i][j]=max(f[i][j],f[i+1][j-k]+g[i][k]);
          cout<<f[1][m]<<endl;
          int j=m;
          for(int i=1;i<=n;i++)
              for(int k=0;k<=j;k++)
                  if(f[i][j]==f[i+1][j-k]+g[i][k]){
                      res[i]=k;
                      j-=k;
                      break;
                  }
          for(int i=1;i<=n;i++)
              cout<<i<<" "<<res[i]<<endl;
          return 0;
      }
      
    • 其他dp

  • 2.2.5 數學與其他

    • 康託展開:

      #include<cstdio>
      using namespace std;
      #define N 1000001
      int n,tr[N];
      long long ans,fac[N];
      void add(int x,int k) {
      	for (; x<=n; x+=x&-x) tr[x]+=k;
      }
      int query(int x) {
      	int t=0;
      	for (; x; x-=x&-x) t+=tr[x];
      	return t;
      }
      int main() {
      	scanf("%d",&n);
      	fac[0]=1;
      	for (int i=1; i<=n; i++) {
      		fac[i]=fac[i-1]*i%998244353;
      		add(i,1);
      	}
      	for (int i=1,x; i<=n; i++) {
      		scanf("%d",&x);
      		ans=(ans+(query(x)-1)*fac[n-i])%998244353;
      		add(x,-1);
      	}
      	printf("%lld",ans+1);
      	return 0;
      }
      
    • 中國剩餘定理:

      #include<bits/stdc++.h>
      using namespace std;
      typedef long long LL;
      const int N = 10;
      int n;
      int A[N], B[N];
      LL exgcd(LL a, LL b, LL &x, LL &y)  // 擴充套件歐幾里得演算法, 求x, y,使得ax + by = gcd(a, b){
      	if (!b){
      		x = 1; y = 0;
      		return a;
      	}
      	LL d = exgcd(b, a % b, y, x);
      	y -= (a / b) * x;
      	return d;
      }
      int main(){
      	scanf("%d", &n);
      	LL M = 1;
      	for (int i = 0; i < n; i ++ ){
      		scanf("%d%d", &A[i], &B[i]);
      		M *= A[i];
      	}
      	LL res = 0;
      	for (int i = 0; i < n; i ++ )
      	{
      		LL Mi = M / A[i];
      		LL ti, x;
      		exgcd(Mi, A[i], ti, x);
      		res = (res + (__int128)B[i] * Mi * ti) % M;
      		// B[i] * Mi * ti可能會超出long long範圍,所以需要轉化成__int128
      	}
      	cout << (res % M + M) % M << endl;
      	return 0;
      }
      
    • 線性篩:

      void work(int n){
      	numlist[1]=1;
      	for(int i=2;i<=n;i++){
      		if(numlist[i]==false) prime[++cnt]=i;
      		for(int j=1; j<=cnt&&i*prime[j]<=n; j++){
      			numlist[i*prime[j]] = true ;
      			if(i%prime[j]==0) break;
      		} 
      	}
      }
      
    • 尤拉篩

      int st[N]; // 初始化為0, 0表示質數,1表示合數
      for(int i = 2; i <= n; i++){
      	for(int j = 2; j <= i / j; j++){
      		if(i % j == 0){
      			st[i] = 1; 
      		}
      	}
      }
      
    • 快速冪:

      int qmi(int a, int k){
      	int res = 1;
      	while (k){
      		if (k & 1) res = (LL)res * a % mod;
      		a = a * a % mod;
      		k >>= 1;
      	}
      	return res;
      }
      
    • 尤拉函式:

      void init(int n)
      {
      	phi[1] = 1;
      	for (int i = 2; i <= n; i ++ )
      	{
      		if (!st[i])
      		{
      			primes[cnt ++ ] = i;
      			phi[i] = i - 1;
      		}
      		for (int j = 0; primes[j] * i <= n; j ++ )
      		{
      			st[i * primes[j]] = true;
      			if (i % primes[j] == 0)
      			{
      				phi[i * primes[j]] = phi[i] * primes[j];
      				break;
      			}
      			phi[i * primes[j]] = phi[i] * (primes[j] - 1);
      		}
      	}
      }
      
      
    • 擴充歐幾里得:

      int exgcd(int a, int b, int &x, int &y)
      {
      	if (!b)
      	{
      		x = 1, y = 0;
      		return a;
      	}
      
      	int d = exgcd(b, a % b, y, x);
      	y -= a / b * x;
      
      	return d;
      }
      
    • 逆元

      • 費馬小定理求逆元:
        LL pow_mod(LL a, LL b, LL p){//a的b次方求餘p 
      	  LL ret = 1;
      	  while(b){
      		  if(b & 1) ret = (ret * a) % p;
      		  a = (a * a) % p;
      		  b >>= 1;
      	  }
      	  return ret;
        }
        LL Fermat(LL a, LL p){//費馬求a關於b的逆元 
      		  return pow_mod(a, p-2, p);
        }
      
      • 擴充套件歐基裡德求逆元:
        #include<cstdio>
        typedef long long LL;
        void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
      	  if (!b) {d = a, x = 1, y = 0;}
      	  else{
      		  ex_gcd(b, a % b, y, x, d);
      		  y -= x * (a / b);
      	  }
        }
        LL inv(LL t, LL p){//如果不存在,返回-1 
      	  LL d, x, y;
      	  ex_gcd(t, p, x, y, d);
      	  return d == 1 ? (x % p + p) % p : -1;
        }
        int main(){
      	  LL a, p;
      	  while(~scanf("%lld%lld", &a, &p)){
      		  printf("%lld\n", inv(a, p));
      	  }
        }
      
      • 線性求逆元:
      	Inv[ 1 ] = 1;
      	for( int i = 2; i <= n; i++ )
      		Inv[ i ] = ( p - p / i ) * Inv[ p % i ] % p;
      
    • 組合數:

      • 楊輝三角:
      #include <iostream>
      using namespace std;
      typedef long long LL;
      const int N = 2010, MOD = 1e9+7;
      int n;
      int c[N][N];
      void init() {
          for (int i = 0; i < N; ++i) 
              for (int j = 0; j <= i; ++j)
                  if (!j) c[i][j] = 1;
                  else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD;
      }
      int main() {
          init();
          int n;
          cin >> n;
          while (n --) {
              int a, b;
              cin >> a >> b;
              cout << c[a][b] << endl;
          }
          return 0;
      }
      
      • 預處理階乘+逆元:
      #include <iostream>
      #include <algorithm>
      using namespace std;
      typedef long long LL;
      const int N = 1e5+5, MOD = 1e9+7;
      int fact[N], infact[N];
      int qmi(int a, int k, int p) {
          LL res = 1;
          while (k) {
              if (k & 1) res = (LL)res * a % p;
              k >>= 1;
              a = (LL)a * a % p;
          }
          return res;
      }
      int main() {
          fact[0] = infact[0] = 1;
          for (int i = 1 ; i < N; ++i) {
              fact[i] = (LL)fact[i - 1] * i % MOD;
              infact[i] = (LL)infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD;
          }
          
          int n;
          cin >> n;
          while (n --) {
              int a, b;
              cin >> a >> b;
              
              cout << (LL)fact[a] * infact[a - b] % MOD * infact[b] % MOD << endl;
          }
          return 0;
      }
      
      • Lucas定理
      #include <iostream>
      using namespace std;
      typedef long long LL;
      int qmi(int a, int k, int p) {
          int res = 1 % p;
          while (k) {
              if (k & 1) res = (LL)res * a % p;
              a = (LL)a * a % p;
              k >>= 1;
          }
          return res;
      }
      // 定義求解
      int C(int a, int b, int p) {
          if (b > a) return 0;
          int res = 1;
          for (int i = 1, j = a; i <= b; ++i, --j) {
              res = (LL)res * j % p;
              res = (LL)res * qmi(i, p - 2, p) % p;
          }
          return res;
      }
      int lucas(LL a, LL b, LL p) {               // 注意LL引數型別
          if (a < p && b < p) return C(a, b, p);
          return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;     // 遞迴讓其到p範圍內求解
      }
      int main() {
          int n;
          cin >> n;
          
          while (n --) {
              LL a, b, p;
              cin >> a >> b >> p;
              cout << (LL)lucas(a, b, p) << endl;
          }
          return 0;
      }
      
    • 高斯消元:

      #include <iostream>
      #include <cmath>
      using namespace std;
      const int N = 105;
      const double eps = 1e-6;
      int n;
      double a[N][N];
      int gauss() {
          int c, r;
          for (c = 0, r = 0; c < n; ++c) {        // c列r行,遍歷列
              int t = r;
              for (int i = r; i < n; ++i)         // 尋找列主元,拿t記錄
                  if (fabs(a[i][c]) > fabs(a[t][c])) 
                      t = i;              
              if (fabs(a[t][c]) < eps) continue;  // 如果列主元為0,不必考慮,當前列全為0
              
              // 交換列主元t行與當前r行的數
              for (int i = c; i < n + 1; ++i) swap(a[t][i], a[r][i]); 
              // 當前列主元已經被交換到了r行,需要從後向前進行處理,避免主對角線元素變成1
              for (int i = n; i >= c; --i) a[r][i] /= a[r][c]; 
              // 列主消元
              for (int i = r + 1; i < n; ++i) 
                  if (fabs(a[i][c]) > eps)
                      for (int j = n; j >= c; --j) 
                          a[i][j] -= a[r][j] * a[i][c];     
              ++r;
          }
          if (r < n) {
              for (int i = r; i < n; ++i) 
                  if (fabs(a[i][n]) > eps) return 2;  // 0x=1 則無解
              
              return 1;   // 0x=0 無窮多解
          }
          // 上三角階梯型矩陣
          // 直接求解即可,最後一列放置結果
          for (int i = n - 1; i >= 0; --i)    
              for (int j = i + 1; j < n; ++j) 
                  a[i][n] -= a[j][n] * a[i][j];
          return 0;
      }
      int main() {
          cin >> n;
          for (int i = 0; i < n; ++i) 
              for (int j = 0; j < n + 1; ++j) 
                  cin >> a[i][j];
          int t = gauss();
          if (t == 0) {
              for (int i = 0; i < n; ++i) printf("%.2lf\n", a[i][n]);
          } 
          else if (t == 1) puts("Infinite group solutions");
          else puts("No solution");
          return 0;
      }
      
    • 龜速乘

      LL qmul(LL a, LL k, LL p) {
          LL res = 0;
          while (k){
              if (k & 1) res = (res + a) % p;
              a = (a + a) % p; 
              k >>= 1;
          }
          return res;
      }
      
    • 莫比烏斯函式

      mu[1]=1;
      for(i=2;i<=n;i++){
          if(!not_prime[i]){
              prime[++tot]=i;
              mu[i]=-1;
          }
          for(j=1;prime[j]*i<=n;j++){
              not_prime[prime[j]*i]=1;
              if(i%prime[j]==0){
                  mu[prime[j]*i]=0;
                  break;
              }
              mu[prime[j]*i]=-mu[i];
          }
      }
      

相關文章