【知识】CSP-S&NOIP模板总结

PS:本文章中大致按照 NOI大纲 顺序编写,如有遗漏的模板,请告知我。

建议打开目录观看。

如需转载请联系我~

作者个人资料

---------------------------------------------------------------------------------------------------------------------------------------

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[N][N],color[N],n, m;
      bool dfs(int u, int c){
          color[u] = c;               
          for (int i = 1; i <= n; i++) {
              if (edge[u][i] == 1) {
                  if (color[i] == c)
                      return false; 
                  if (!color[i] && !dfs(i, -c))
                      return false; 
              }
          }
          return true; 
      }
      
    • P3386 【模板】二分图最大匹配

      #include <iostream>
      #include <cstring>
      using namespace std;
      const int N = 10005, M = 200005;
      
      int h[N], ne[M], cnt, e[M];
      int match[N],vis[N];
      int ans;
      void add(int a,int b){
          e[++cnt] = b;
          ne[cnt] = h[a];
          h[a] = cnt;
      }
      
      bool dfs(int x){
          for (int i = h[x]; i;i=ne[i]){
              int to = e[i];
              if(vis[to])
                  continue;
              vis[to] = 1;
              if(match[to]==0||dfs(match[to])){
                  match[to] = x;
                  return true;
              }
          }
          return false;
      }
      int main(){
          int n, m, e;
          cin >> n >> m >> e;
          for (int i = 1; i <= e;i++){
              int u, v;
              cin >> u >> v;
              add(u, v + n);
              add(v + n, u);
          }
      
          for (int i = 1; i <= n;i++){
              memset(vis, 0, sizeof(vis));
              if(dfs(i))
                  ans++;
          }
          cout << ans << endl;
          return 0;
      }
      
    • P7771 【模板】欧拉路径

      #include <bits/stdc++.h>
      using namespace std;
      
      const int N = 100005;
      
      int n, m, u, v, d[N], din[N],dout[N];
      stack<int> st;
      vector<int> G[N];
      
      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);
              dout[u]++;
              din[v]++;
          }
      
          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 (dout[i]!= din[i])
              {
                  flag = 0;
                  if (dout[i] - din[i] == 1)
                      cnt[1]++, S = i;
                  else if (din[i] - dout[i] == 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())
              cout << 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], cnt;
      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[++cnt] = b;
          ne[cnt] = h[a];
          h[a] = cnt;
      }
      
      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(){
          cin >> n >> m;
          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()
      {
          cin >> n >> m;
          for (int i = 1; i <= m; i++){
              int u, v;
              cin >>u >> v;
              add(u, v);
              add(v, u);
          }
          for (int i = 1; i <= n; i++){
              if (dfn[i]) continue;
              top = 0;
              tarjan(i, 0);
          }
          cout << bcc << endl;
          for (int i = 1; i <= bcc; i++){
              cout << ans[i].size() << " ";
              for (int j : ans[i])
                  cout << j << " ";
              cout << endl;
          }
          return 0;
      }
      
    • DAG拓扑排序:

      #include <iostream>
      using namespace std;
      const int N=100005;
      int du[N], h[N], e[N], ne[N],cnt;
      int n;
      
      int q[N];
      void add(int a,int b){
          e[++cnt] = b;
          ne[cnt] = h[a];
          h[a] = cnt;
      }
      
      void topo(){
          int hh = 0, tt = -1;
          for (int i = 1; i <= n;i++) if(!du[i])
                  q[++tt] = i;
          while(hh<=tt){
              int u = q[hh++];
              for (int i = h[u]; i;i=ne[i]){
                  if(!--du[e[i]])
                      q[++tt] = e[i];
              }
              
          }
          for (int i = 0; i <= tt;i++)
              cout << q[i] << " ";
      }
      int main(){
      
          cin >> n;
          for (int i = 1; i <= n;i++){
              int a;
              while(cin>>a && a){
                  add(i, a);
                  du[a]++;
              }
          }
      
          topo();
          return 0;
      }
      
    • Dijkstra \(O((n+m)\times log(n+m))\)

      #include <bits/stdc++.h>
      using namespace std;
      const int N = 200005;
      int n, m, s;
      vector<pair<int, int>> v[N];
      int dis[N];
      bool vis[N];
      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;
              if(vis[id]) continue;
              vis[id]=1;
              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});
                  }
              }
          }
      }
      int main(){
          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] << " ";
      }
      
      
    • SPFA:

      #include <bits/stdc++.h>
      using namespace std;
      queue<int> Q;
      const int N = 100005, INF = 0x3f3f3f3f;
      int dis[N];
      bool vis[N];
      int n, m, s,head[N], pos;
      struct edge{
          int to, next, c;
      } e[N];
      void add(int a, int b, int c){
          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:
     ```cpp
     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[N][N];        //dis[k][v];表示选取前k个时到达i的最短距离
      struct Edge{
          int u, v, w;
      }edge[N];
      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){
          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];  // n是点数,m是边数,d[N]是深度数组
      bool vis[N];  // vis数组用于标记节点是否被访问过
      ll f[N][25], g1[N][25], g2[N][25], mst, ans = INF;
      // f[N][25]用于存储每个节点的祖先,g1[N][25]存储到祖先路径中的最大边权,g2[N][25]存储到祖先路径中的次大边权
      struct Node {
          ll to, cost;  // 节点to和到该节点的边权cost
      };
      vector<Node> v[N];  // 邻接表,用于存储树的结构
      
      // 深度优先搜索(DFS)用于构建树的深度、祖先及边权数组
      void dfs(const int x) {
          vis[x] = true;  // 标记当前节点已访问
          for (int i = 0; i < v[x].size(); i++) {
              int y = v[x][i].to;  // 获取相邻节点y
              if (vis[y]) continue;  // 如果y已经被访问,跳过
              d[y] = d[x] + 1;  // 更新节点y的深度
              f[y][0] = x;  // 更新节点y的第一个祖先为x
              g1[y][0] = v[x][i].cost;  // 更新边权数组g1[y][0]
              g2[y][0] = -INF;  // 初始次大边权设为-INF
              dfs(y);  // 递归访问节点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];  // 动态规划计算j节点的第i个祖先
                  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]);
              }
      }
      
      // 找出节点x和y的最低公共祖先(LCA),并更新次小生成树的结果
      inline void LCA(int x, int y, const ll w) {
          ll zui = -INF, ci = -INF;  // zui存储路径中最大边权,ci存储次大边权
          if (d[x] > d[y]) swap(x, y);  // 保证x的深度小于或等于y的深度
          // 使y节点上升到与x同样深度
          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) {  // 如果x和y是同一节点
              if (zui != w) 
                  ans = min(ans, mst - zui + w);  // 更新答案为当前次小生成树
              else if (ci != w && ci > 0) 
                  ans = min(ans, mst - ci + w);  // 如果次大边权符合条件,更新答案
              return;
          }
          // 同时让x和y向上走,直到找到公共祖先
          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];
              }
          // 最后一步,x和y必然是各自的第一个祖先
          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];  // 并查集数组,用于Kruskal算法
      
      // 并查集的查找函数,带路径压缩
      inline int find(const int x) {
          if (fa[x] == x) return x;
          else return fa[x] = find(fa[x]);
      }
      
      // Kruskal算法,用于求最小生成树
      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;  // 如果x和y已经连通,跳过
              fa[find(x)] = y;  // 将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;  // 将根节点的深度设为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;
      }
      

      Tarjan 离线求 LCA

      #include <cstdio>
      #include <cstring>
      #include <iostream>
      #include <algorithm>
      #include <vector>
      
      using namespace std;
      
      typedef pair<int, int> PII;
      
      const int N = 10010, M = N * 2;
      
      int n, m;
      int h[N], e[M], w[M], ne[M], idx;
      int dist[N];
      int p[N];
      int res[M];
      int st[N];
      vector<PII> query[N];   // first存查询的另外一个点,second存查询编号
      
      void add(int a, int b, int c)
      {
          e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
      }
      
      void dfs(int u, int fa)
      {
          for (int i = h[u]; ~i; i = ne[i])
          {
              int j = e[i];
              if (j == fa) continue;
              dist[j] = dist[u] + w[i];
              dfs(j, u);
          }
      }
      
      int find(int x)
      {
          if (p[x] != x) p[x] = find(p[x]);
          return p[x];
      }
      
      void tarjan(int u)
      {
          st[u] = 1;
          for (int i = h[u]; ~i; i = ne[i])
          {
              int j = e[i];
              if (!st[j])
              {
                  tarjan(j);
                  p[j] = u;
              }
          }
      
          for (auto item : query[u])
          {
              int y = item.first, id = item.second;
              if (st[y] )
              {
                  int anc = find(y); 
                  res[id] = anc;
              }
          }
      
      }
      
      int main()
      {
          scanf("%d%d", &n, &m);
          memset(h, -1, sizeof h);
          for (int i = 0; i < n - 1; i ++ )
          {
              int a, b, c;
              scanf("%d%d%d", &a, &b, &c);
              add(a, b, c), add(b, a, c);
          }
      
          for (int i = 0; i < m; i ++ )
          {
              int a, b;
              scanf("%d%d", &a, &b);
              if (a != b)
              {
                  query[a].push_back({b, i});
                  query[b].push_back({a, i});
              }
          }
      
          for (int i = 1; i <= n; i ++ ) p[i] = i;
      
          dfs(1, -1);
          tarjan(1);
      
          for (int i = 0; i < m; i ++ ) printf("%d\n", res[i]);
      
          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));
      	  }
        }
      
      • 欧拉定理求逆元:
        #include <bits/stdc++.h>
        using namespace std;
        const int N = 1e6 + 10;
        vector<int> prime;
        int phi[N];
        bool st[N];
      
        void getphi(int n)
        {
            memset(st, 1, sizeof st);
            for (int i = 2; i <= n; i++)
            { //遍历区间内所有值
                if (st[i])
                    prime.push_back(i), phi[i] = i - 1; //性质1
                for (int j = 0; j < prime.size() && prime[j] <= n / i; j++)
                {
                    int p = prime[j];
                    st[p * i] = 0;
                    if (i % p == 0)
                    {                            //i 和 p的位置不要搞反
                        phi[p * i] = phi[i] * p; //性质3
                        break;
                    }
                    phi[i * p] = phi[i] * (p - 1); //性质2+性质1
                }
            }
        }
        int main()
        {
            int n;
            getphi(N);
            while (cin >> n, n)
            {
                cout << phi[n] << endl;
            }
            return 0;
        }
      
      • 线性求逆元:
      	Inv[ 1 ] = 1;
      	for( int i = 2; i <= n; i++ )
      		Inv[ i ] = ( p - p / i ) * Inv[ p % i ] % p;
      
      • 线性求阶乘逆元:
          int inv( int b, int p ) {
          int a, k;
          exPower( b, p, a, k );
          if( a < 0 ) a += p;
          return a;
      }
      void init( int n ) {
          Fact[ 0 ] = 1;
          for( int i = 1; i <= n; ++i ) Fact[ i ] = Fact[ i - 1 ] * i % Mod;
          INV[ n ] = inv( Fact[ n ], Mod );
          for( int i = n - 1; i >= 0; --i ) INV[ i ] = INV[ i + 1 ] * ( i + 1 ) % Mod;
          return;
      }
      
    • 组合数:

      • 杨辉三角:
      #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];
          }
      }
      

============================================

失配树

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

#define ll long long
#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 DEBUG(x) cerr << #x << '=' << x << endl

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

void print(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
    return;
}

namespace Star_F{
    const int kmaxlog = 25;
    const int kmaxn = 1000000 + 5;
    const int ml = 21;
    int fail[kmaxn];
    int fa[kmaxn][kmaxlog];
    int dis[kmaxn];
    char s[kmaxn];
    int n, q;

    void init() {
        int p = 0;
        FOR(i, 2, n) {
            while (p && s[p + 1] != s[i])
                p = fail[p];
            if (s[p + 1] == s[i]) ++p;
            fail[i] = p;
            dis[i] = dis[p] + 1;
            fa[i][0] = p;
        }
        FOR(k, 1, ml) {
            FOR(i, 1, n)
                fa[i][k] = fa[fa[i][k - 1]][k - 1];
        }
    }

    int jmp(int x, int h) {
        if (dis[x] <= h) return x;
        ROF(i, ml, 0) {
            if (dis[fa[x][i]] > h)
                x = fa[x][i];
        }
        return fa[x][0];
    }

    int lca(int x, int y) {
        if (dis[x] < dis[y]) swap(x, y);
        x = jmp(x, dis[y]);
        ROF(i, ml, 0) {
            if (fa[x][i] != fa[y][i]) {
                x = fa[x][i];
                y = fa[y][i];
            }
        }
        return fa[x][0];
    }

    void Main() {
        scanf("%s%d", &s[1], &q);
        n = strlen(s + 1);
        init();
        int a, b;
        while (q--) {
            scanf("%d%d", &a, &b);
            printf("%d\n", lca(a, b));
        }
    }
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    return Star_F::Main(), 0;
}

无旋Ttreap

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

#define ll long long
#define MAX 100100

inline int read() {
    int x = 0;
    bool neg = false;
    char ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') {
        neg = true;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch - '0');
        ch = getchar();
    }
    return neg ? -x : x;
}

void print(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

namespace Star_F {

    const int N = 1e6 + 1e5 + 1;

    unsigned long long seed = 1;
    std::mt19937 rnd(std::random_device{}());

    struct fhq {
        struct {
            int l, r;  // 左右孩子
            int val;   // BST 的权值
            int rnd;   // 堆的随机值
            int size;  // 树的大小
        } tr[N];
        int root = 0, n = 0;  // 根节点, 最新节点的下标

        inline int _rand() { return rnd(); }

        // 创建一个新节点
        int newnode(int v) {
            tr[++n].val = v;
            tr[n].rnd = _rand();
            tr[n].size = 1;
            return n;
        }

        // 更新树的大小
        void pushup(int p) { tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1; }

        // 按值 v 进行分裂, 左子树 <=v, 右子树 > v
        // x, y 是分裂后的两颗子树的根, 执行完成后 x.val <= v < y.val
        void split(int p, int v, int &x, int &y) {
            if (!p) {  // 空树
                x = y = 0;
                return;
            }
            if (tr[p].val <= v) {
                // 递归分裂 p 的右子树
                // 左子树的根 x 已确定, y 需要继续向下带
                // 同时, p 的右节点需要指向下一层指向的左子树的根 x
                x = p;
                split(tr[p].r, v, tr[p].r, y);
            } else {  // 递归分裂 p 的左子树
                y = p;
                split(tr[p].l, v, x, tr[p].l);
            }

            // 最后, 向上更新每个子树的大小
            pushup(p);
        }

        // 按堆的随机值合并两颗子树, 返回合并后的根
        // 要求 x 树所有节点的 val 要 <= y 树所有节点的 val 值
        int merge(int x, int y) {
            if (!x || !y) return x + y;  // 存在空树的情况
            if (tr[x].rnd < tr[y].rnd) {
                // 应把 x 放上面, 先递归合并 x 的右子树 r 和 y 得到新树 z
                // 因为 x 权值更小, 所以把 z 作为 x 的右孩子
                tr[x].r = merge(tr[x].r, y);
                pushup(x);
                return x;
            } else {
                tr[y].l = merge(x, tr[y].l);
                pushup(y);
                return y;
            }
        }
        explicit fhq() { memset(tr, 0, sizeof tr); }

        int size() { return tr[root].size; }

        // 插入一个值
        void insert(int v) {
            // 按 v 分裂, 找到插入点 x <=v < y
            int x, y;
            split(root, v, x, y);
            // 新建节点
            int z = newnode(v);
            // 依次合并 x, z 和 y (权值 val 也满足如此顺序)
            root = merge(merge(x, z), y);
        }

        // 删除一个值
        void del(int v) {
            int x, y, z;
            // 先找到 v 的分割点 => x <= v < z
            split(root, v, x, z);
            // 再按 v-1 分裂 x 树 => x <= v-1 < y <= v
            split(x, v - 1, x, y);
            // y 就是值等于 v 的节点, 删除它
            // 如果找不到 v, y 就是空树, 其左右孩子都是 0, 不影响
            y = merge(tr[y].l, tr[y].r);
            // 再把 x, y, z 合并起来
            root = merge(merge(x, y), z);
        }

        // 查找排名, 满足 < v 的个数+1
        int rank(int v) {
            int x, y;
            split(root, v - 1, x, y);
            int ans = tr[x].size + 1;
            root = merge(x, y);
            return ans;
        }

        // 从子树 p 找第 k 小
        int topk(int p, int k) {
            int lsz = tr[tr[p].l].size;
            if (k == lsz + 1) return tr[p].val;
            if (k <= lsz) return topk(tr[p].l, k);
            return topk(tr[p].r, k - lsz - 1);
        }

        // 前驱, 严格 <v 的值
        int get_pre(int v) {
            int x, y;
            split(root, v - 1, x, y);
            int ans = topk(x, tr[x].size);
            root = merge(x, y);
            return ans;
        }

        // 后继, 严格 > v 的值
        int get_suc(int v) {
            int x, y;
            split(root, v, x, y);
            int ans = topk(y, 1);
            root = merge(x, y);
            return ans;
        }
    };

    void Main() {
        int n = read();
        int m = read();
        // 初始化
        fhq t;
        int val;
        for (int i = 0; i < n; i++) {
            val = read();
            t.insert(val);
        }
        // 操作
        int last = 0, ans = 0;
        while (m--) {
            int opt = read();
            int x = read();
            x ^= last;
            switch (opt) {
                case 1:
                    t.insert(x);
                    break;
                case 2:
                    t.del(x);
                    break;
                case 3:
                    last = t.rank(x);
                    ans ^= last;
                    break;
                case 4:
                    last = t.topk(t.root, x);
                    ans ^= last;
                    break;
                case 5:
                    last = t.get_pre(x);
                    ans ^= last;
                    break;
                case 6:
                    last = t.get_suc(x);
                    ans ^= last;
                    break;
            }
        }
        print(ans);
        putchar('\n');
    }
}

signed main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    return Star_F::Main(), 0;
}

P2042 [NOI2005] 维护数列 (含区间操作的无旋Treap)

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 500005;

unsigned long long seed = 1;

// 要插入的数字
const int MAX_INSERTS = 4000005;
int to_inserts[MAX_INSERTS];

#define ls tr[p].l
#define rs tr[p].r

struct {
    int l, r;  // 左右孩子
    int val;   // 节点权值
    int rnd;   // 随机值
    int size;  // 子树大小

    int rev_tag;  // 翻转延迟标记
    int cov_tag;  // 覆盖延迟标记
    int cov_val;  // 覆盖更新的值

    int sum;   // 子树的区间和
    int mpre;  // 区间最大前缀和
    int msuf;  // 区间最大后缀和
    int msum;  // 子树内最大的区间和
} tr[N];

int root = 0, tt = 0;

// 垃圾回收栈
int gc_stack[N];
int gc_top = 0;

inline int _rand() {
    seed *= 333333;
    return int(seed);
}

int newnode(int val) {
    int i = gc_top ? gc_stack[--gc_top] : ++tt;
    memset(&tr[i], 0, sizeof tr[i]);
    tr[i].rnd = _rand();
    tr[i].size = 1;
    tr[i].msum = tr[i].sum = tr[i].val = val;
    tr[i].mpre = tr[i].msuf = max(0, val);
    return i;
}

void gc(int p) {
    if (!p) return;
    gc_stack[gc_top++] = p;
    gc(ls), gc(rs);
}

void pushup(int p) {
    tr[p].size = tr[ls].size + tr[rs].size + 1;
    tr[p].sum = tr[ls].sum + tr[rs].sum + tr[p].val;

    // 父区间的前缀和要么是左子区间的, 要么是 l.sum + p + r.mpre
    tr[p].mpre = max({tr[ls].mpre, tr[ls].sum + tr[p].val + tr[rs].mpre, 0});
    // 同理
    tr[p].msuf = max({tr[rs].msuf, tr[rs].sum + tr[p].val + tr[ls].msuf, 0});

    // 路过 p 的情况
    tr[p].msum = max(tr[ls].msuf + tr[p].val + tr[rs].mpre, tr[p].val);
    // 不路过 p 的情况,取最大
    if (ls) tr[p].msum = max(tr[p].msum, tr[ls].msum);
    if (rs) tr[p].msum = max(tr[p].msum, tr[rs].msum);
}

// 执行 p 区间的覆盖
void do_cover(int p) {
    if (!tr[p].cov_tag) return;

    // 修改值
    int c = tr[p].val = tr[p].cov_val;

    // 因为采用延迟更新
    // 所以子树的和不再等于: 左右子树的和+p.val
    // 这里要同步修改掉
    int s = tr[p].sum = c * tr[p].size;
    tr[p].mpre = tr[p].msuf = max(0, s);
    tr[p].msum = max(c, s);

    // 下传标记
    tr[ls].cov_tag = 1, tr[ls].cov_val = c;
    tr[rs].cov_tag = 1, tr[rs].cov_val = c;

    // 清理当前标记
    tr[p].cov_tag = 0;
    tr[p].cov_val = 0;
}

void do_reverse(int p) {
    if (!tr[p].rev_tag) return;
    // 翻转
    swap(ls, rs);
    // 还要翻转前后缀和
    swap(tr[p].mpre, tr[p].msuf);
    // 向下标记
    tr[ls].rev_tag ^= 1;
    tr[rs].rev_tag ^= 1;
    // 清理当前标记
    tr[p].rev_tag = 0;
}

void pushdown(int p) {
    if (!p) return;

    // 区间翻转
    do_reverse(p);
    // 向下标记, 多执行一层
    if (ls) do_reverse(ls);
    if (rs) do_reverse(rs);

    // 区间覆盖
    do_cover(p);
    // 区间覆盖要多向前执行一层
    // 因为左右子树其中一个变化, 另一半也要变化
    // 这样 sum 查询才是准的
    if (ls) do_cover(ls);
    if (rs) do_cover(rs);
}

// 按位置分裂,左边的树位置 [1,k]
void split(int p, int k, int &x, int &y) {
    if (!p) {
        x = y = 0;
        return;
    }
    pushdown(p);
    if (tr[ls].size < k) {
        x = p;
        k -= tr[ls].size + 1;
        split(rs, k, rs, y);
    } else {
        y = p;
        split(ls, k, x, ls);
    }

    pushup(p);
}

int merge(int x, int y) {
    if (!x || !y) return x + y;
    if (tr[x].rnd < tr[y].rnd) {
        pushdown(x);
        tr[x].r = merge(tr[x].r, y);
        pushup(x);
        return x;
    } else {
        pushdown(y);
        tr[y].l = merge(x, tr[y].l);
        pushup(y);
        return y;
    }
}

// 追加一个新数字
void append(int val) { root = merge(root, newnode(val)); }

// 对 to_inserts 二分建树
int build(int l, int r) {
    if (l == r) return newnode(to_inserts[l]);
    int mid = (l + r) >> 1;
    return merge(build(l, mid), build(mid + 1, r));
}

// 在 k 处插入 tot 个数字
// k = 0 时在队头插入
void insert(int k, int tot) {
    int x, y;
    split(root, k, x, y);
    int z = build(1, tot);
    root = merge(merge(x, z), y);
}

// 朴素做法,TEL
void insert1(int k, int tot) {
    int x, y;
    split(root, k, x, y);
    for (int i = 1; i <= tot; i++) x = merge(x, newnode(to_inserts[i]));
    root = merge(x, y);
}

// 从第 k 个开始, 切出子树 x, y, z
void help_split(int &x, int &y, int &z, int k, int tot) {
    int l = k, r = k + tot - 1;
    split(root, r, x, z);
    split(x, l - 1, x, y);
}

// 在 k 处删除 tot 个数字
void del(int k, int tot) {
    int x, y, z;
    help_split(x, y, z, k, tot);
    gc(y);
    root = merge(x, z);
}

// 在 k 处翻转 tot 个
void reverse(int k, int tot) {
    int x, y, z;
    help_split(x, y, z, k, tot);
    // 打标
    tr[y].rev_tag ^= 1;
    // 执行一层
    do_reverse(y);
    root = merge(merge(x, y), z);
}

// 从当前数列的第 k 个数字开始的连续 tot 个数字统一修改为 c
void makesame(int k, int tot, int c) {
    int x, y, z;
    help_split(x, y, z, k, tot);
    // 打标
    tr[y].cov_tag = 1;
    tr[y].cov_val = c;
    // 执行一层
    do_cover(y);
    root = merge(merge(x, y), z);
}

// 计算从当前数列的第 k 个数字开始的 tot 个数字的和并输出
int getsum(int k, int tot) {
    int x, y, z;
    help_split(x, y, z, k, tot);
    int ans = tr[y].sum;
    root = merge(merge(x, y), z);
    return ans;
}

// 求出当前数列中和最大的一段子列,并输出最大和
int maxsum() { return tr[root].msum; }

void output(int p) {
    if (!p) return;
    pushdown(p);
    output(tr[p].l);
    printf("%d ", tr[p].val);
    output(tr[p].r);
}

int main(void) {
    int n, m;
    scanf("%d%d", &n, &m);
    // 初始化
    for (int i = 1; i <= n; i++) {
        int val;
        scanf("%d", &val);
        append(val);
    }
    // 读取操作
    char op[10];
    while (m--) {
        scanf("%9s", op);
        int k, tot, c, ans;
        switch (op[0]) {
            case 'I':  // 插入
                scanf("%d%d", &k, &tot);
                for (int i = 1; i <= tot; i++) scanf("%d", &to_inserts[i]);
                insert(k, tot);
                break;
            case 'D':  // 删除
                scanf("%d%d", &k, &tot);
                del(k, tot);
                break;
            case 'R':  // 翻转
                scanf("%d%d", &k, &tot);
                reverse(k, tot);
                break;
            case 'G':  // 区间求和
                scanf("%d%d", &k, &tot);
                ans = getsum(k, tot);
                printf("%d\n", ans);
                break;
            case 'M':
                if (op[2] == 'K') {  // 覆盖
                    scanf("%d%d%d", &k, &tot, &c);
                    makesame(k, tot, c);
                } else {  // 求最大子列和
                    ans = maxsum();
                    printf("%d\n", ans);
                }
                break;
        }
    }
    return 0;
}

P5905 【模板】全源最短路(Johnson)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#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 DEBUG(x) cerr << #x << '=' << x << endl

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

void print(ll x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
    return;
}

namespace Star_F {

struct edge {
    int v, w, next;
} e[10005];

struct node {
    int dis, id;
    bool operator<(const node& a) const { return dis > a.dis; }
    node(int d, int x) { dis = d, id = x; }
};

const int INF = 1e9;
int head[5005], vis[5005], t[5005];
int cnt, n, m;
long long h[5005], dis[5005];

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(h, 63, sizeof(h));
    h[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 (h[v] > h[u] + e[i].w) {
                h[v] = h[u] + e[i].w;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                    t[v]++;
                    if (t[v] == n + 1) return false;
                }
            }
        }
    }
    return true;
}

void dijkstra(int s) {
    priority_queue<node> q;
    for (int i = 1; i <= n; i++) dis[i] = INF;
    memset(vis, 0, sizeof(vis));
    dis[s] = 0;
    q.push(node(0, s));
    while (!q.empty()) {
        int u = q.top().id;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        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]) q.push(node(dis[v], v));
            }
        }
    }
    return;
}

void Main() {
    n = rd(); m = rd();
    for (int i = 1; i <= m; i++) {
        int u = rd(), v = rd(), w = rd();
        addedge(u, v, w);
    }
    for (int i = 1; i <= n; i++) addedge(0, i, 0);
    if (!spfa(0)) {
        cout << -1 << endl;
        return;
    }
    for (int u = 1; u <= n; u++)
        for (int i = head[u]; i; i = e[i].next) e[i].w += h[u] - h[e[i].v];
    for (int i = 1; i <= n; i++) {
        dijkstra(i);
        long long ans = 0;
        for (int j = 1; j <= n; j++) {
            if (dis[j] == INF)
                ans += j * INF;
            else
                ans += j * (dis[j] + h[j] - h[i]);
        }
        cout << ans << endl;
    }
}

}

signed main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    return Star_F::Main(), 0;
}

整除分块

#include <bits/stdc++.h>

using namespace std;

int main(){
    int n;
    cin >> n;
    for (int l = 1, r; l <= n;l=r+1){
        r = n / (n / l);
        cout << l << "~" << r << ":" << n/l << endl;
    }
    return 0;
}

P3846 [TJOI2007] 可爱的质数/【模板】BSGS

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

#define ll long long
#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 DEBUG(x) cerr << #x << '=' << x << endl
#define int ll
#define HASH_MOD 76799777LL

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

void print(ll x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

namespace Star_F {
    map<int, int> hash;

    ll qpow(ll A, ll B, ll C) {
        if (B == 0) return 1;
        if (B == 1) return A;
        ll t = qpow(A, B >> 1, C);
        t = t * t % C;
        if (B & 1) t = t * A % C;
        return t;
    }

    ll BSGS(ll A, ll B, ll C) {
        const int sizes = ceil(sqrt(C));
        ll base = B % C;
        hash[base] = 0;
        for (int i = 1; i <= sizes; i++) {
            base = base * A % C;
            hash[base] = i;
        }
        base = qpow(A, sizes, C);
        ll tmp = 1;
        for (ll i = 1; i <= sizes; i++) {
            tmp = tmp * base % C;
            if (hash[tmp])
                return ((i * sizes - hash[tmp]) % C + C) % C;
        }
        return -1;
    }

    ll P, B, N;

    void Main() {
        P = rd();
        B = rd();
        N = rd();
        if (!(B % P)) {
            printf("no solution\n");
            return;
        }
        ll ans = BSGS(B, N, P);
        if (ans != -1) printf("%lld", ans);
        else printf("no solution");
    }
}

signed main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    return Star_F::Main(), 0;
}

补充:

树状数组求逆序对

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 500005;
int m;
int a[N], b[N], c[N];
int lowbit(int x){
    return x & (-x);
} 
void add(int x, int y)
{
    for (int i = x; i <= m; i+=lowbit(i))
        c[i] += y;
}

int sum(int x)
{
    int res = 0;
    for (int i = x; i; i -= lowbit(i))
        res += c[i];
    return res;
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i], b[i] = a[i];
    sort(b + 1, b + n + 1);
    m = unique(b + 1, b + n + 1) - b - 1;
    long long ans = 0;
    for (int i = n; i; i--)
    {
        int k = lower_bound(b + 1, b + m + 1, a[i]) - b;
        ans += sum(k - 1);
        add(k, 1);
    }

    cout << ans << endl;
    return 0;
}

树的重链剖分:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 100005;
int n, m, r, p, vistie;
int a[N], fa[N], wc[N], sz[N], top[N], dfn[N], rdfn[N], dep[N];

vector<int> e[N];

void dfs1(int u,int f){
    fa[u] = f;
    sz[u] = 1;
    dep[u] = dep[f] + 1;
    for (int i = 0; i < e[u].size();i++){
        if(e[u][i]!=f){
            int v = e[u][i];
            dfs1(v, u);
            sz[u] += sz[v];
            if(sz[v]>sz[wc[u]])
                wc[u] = v;
        }
    }
}

void dfs2(int u,int Top){
    dfn[u] = ++vistie;
    rdfn[vistie] = u;
    top[u] = Top;
    if(wc[u]!=0){
        dfs2(wc[u], Top);
        for (int i = 0; i < e[u].size();i++){
            if(e[u][i]!=fa[u]&&e[u][i]!=wc[u]){
                dfs2(e[u][i], e[u][i]);
            } 
        }
    }
}

ll w[N * 4];
void pushup(int u){
    w[u] = (w[u << 1] + w[u << 1 | 1]) % p;
}

void build(int u,int L,int R){
    if(L==R){
        w[u] = a[rdfn[L]];
        return;
    }
    int mid = L + R >> 1;
    build(u << 1, L, mid);
    build(u << 1 | 1, mid + 1, R);
    pushup(u);
}

bool InRange(int L,int R,int l,int r){
    return (l <= L) && (R <= r);
}

bool OutoRange(int L,int R,int l,int r){
    return (L > r) || (R < l);
}

ll lzy[N * 4];

void maketag(int u,int len,int x){
    lzy[u] = (lzy[u] + x) % p;
    w[u] = (w[u] + len * x) % p;
}

void pushdown(int u,int L,int R){
    int mid = L + R >> 1;
    maketag(u << 1, mid - L + 1, lzy[u]);
    maketag(u << 1 | 1, R - mid, 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 mid = L + R >> 1;
        pushdown(u, L, R);
        return (query(u << 1, L, mid, l, r) + query(u << 1 | 1, mid + 1, R, l, r) )% p;
    }
    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 mid = L + R >> 1;
        pushdown(u, L, R);
        update(u << 1, L, mid, l, r, x);
        update(u << 1 | 1, mid + 1, R, l, r, x);
        pushup(u);
    }
}

void upd(int x,int y,int z){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])
            swap(x, y);
        update(1, 1, n, dfn[top[x]], dfn[x], z);
        x = fa[top[x]];
    }
    update(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), z);
}

int qry(int x,int y){
    ll ans = 0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])
            swap(x, y);
        
        ans += query(1, 1, n, dfn[top[x]], dfn[x]);
        ans %= p;
        x = fa[top[x]];
    }
    return (ans + query(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]))) % p;
}
int main(){
    cin >> n >> m >> r >> p;
    for (int i = 1;i<=n;i++)
        cin >> a[i];
    for (int i = 1; i < n;i++){
        int u, v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(r, 0);
    dfs2(r, 0);
    build(1, 1, n);
    while(m--){
        int o, x, y, z;
        cin >> o;
        if(o==1){
            cin >> x >> y >> z;
            upd(x, y, z);
        }
        else if(o==2){
            cin >> x >> y;
            cout << qry(x, y) << endl;
        }
        else if(o==3){
            cin >> x >> y;
            update(1, 1, n, dfn[x], dfn[x] + sz[x] - 1, y);
        }
        else{
            cin >> x;
            cout << query(1, 1, n, dfn[x], dfn[x] + sz[x] - 1) % p << endl;
        }
    }
    return 0;
}
posted @ 2024-07-28 17:29  Star_F  阅读(618)  评论(2编辑  收藏  举报