[关闭]
@Apojacsleam 2018-11-09T04:45:47.000000Z 字数 150724 阅读 8225

NOIP2018板子合集

模板 NOIP 2018 比赛 提高组


写在前面

本文旨在用于NOIP2018考前复习。
时间原因,作者无法一一亲自调试其中的程序,也因如此,有一部分程序来自于互联网,如果您觉得这侵犯了您的合法权益,请联系Apojacsleam(QQ1729338463)删除
对于给您造成的不便和困扰,我表示深深的歉意。
本文仅用于学习,禁止任何人或组织用于商业用途
本文中,标记*的为选学算法,在NOIP中较少涉及。

读入优化与输出优化

读入优化

朴素读入优化

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. #include<cctype>
  6. int readin()
  7. {
  8. int res=0;char ch=getchar();bool XX=false;
  9. for(;!isdigit(ch);ch=getchar())
  10. (ch=='-') && (XX=true);
  11. for(;isdigit(ch);ch=getchar())
  12. res=(res<<3)+(res<<1)+(ch^48);
  13. return XX?-res:res;
  14. }

fread读入优化

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. #include<cctype>
  6. inline char getcha(){
  7. static char buf[100000],*p1=buf,*p2=buf;
  8. return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
  9. }
  10. int readin()
  11. {
  12. int res=0;char ch=getcha();bool XX=false;
  13. for(;!isdigit(ch);ch=getcha())
  14. (ch=='-') && (XX=true);
  15. for(;isdigit(ch);ch=getcha())
  16. res=(res<<3)+(res<<1)+(ch^48);
  17. return XX?-res:res;
  18. }

实数读入优化

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. #include<cctype>
  6. inline double dbread()
  7. {
  8. double X=0,Y=1.0; int w=0; char ch=0;
  9. while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
  10. while(isdigit(ch)) X=X*10+(ch^48),ch=getchar();
  11. ch=getchar();//读入小数点
  12. while(isdigit(ch)) X+=(Y/=10)*(ch^48),ch=getchar();
  13. return w?-X:X;
  14. }

输出优化

朴素输出优化

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. void writeout(int x)
  6. {
  7. if(x<0) putchar('-'),x=-x;
  8. if(x>9) writeout(x/10);
  9. putchar(x%10+48);
  10. }

fwrite输出优化

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. char pbuf[100000],*pp=pbuf;
  6. void push(const char c) {
  7. if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf;
  8. *pp++=c;
  9. }
  10. void write(int x)
  11. {
  12. static int sta[35];
  13. int top=0;
  14. do{sta[top++]=x%10,x/=10;}while(x);
  15. while(top) push(sta[--top]+'0');
  16. }
  17. //请大家在程序结束前加上一句fwrite(pbuf,1,pp-pbuf,stdout);pp=pbuf;
  18. //防止出现没输出完成的类似错误

数论算法模板

欧几里得算法

【简介】

欧几里德算法又称辗转相除法,是指用于计算两个正整数的最大公约数。应用领域有数学和计算机两个方面。计算公式

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://www.codevs.cn/problem/1212
  5. #include<cstdio>
  6. int gcd(int a,int b)
  7. {
  8. return b?gcd(b,a%b):a;
  9. }
  10. int a,b;
  11. int main()
  12. {
  13. scanf("%d%d",&a,&b);
  14. printf("%d",gcd(a,b));
  15. }

复杂度

扩展欧几里得算法

【简介】

扩展欧几里德算法是用来在已知求解一组,使它们满足贝祖等式: (解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1082
  5. #include<cstdio>
  6. int exgcd(int a,int b,int &x,int &y)
  7. {
  8. if(!b)
  9. {
  10. x=1,y=0;
  11. return a;
  12. }
  13. int gcd=exgcd(b,a%b,x,y),t=x;
  14. x=y,y=t-(a/b)*x;
  15. return gcd;
  16. }
  17. int a,b,x,y;
  18. int main()
  19. {
  20. scanf("%d%d",&a,&b);
  21. exgcd(a,b,x,y);
  22. printf("%d",(x%b+b)%b);
  23. return 0;
  24. }

复杂度

线性复杂度求逆元

【简介】

乘法逆元,是指数学领域群中任意一个元素,都在中有唯一的逆元,具有性质,其中为该群的单位元。

【代码实现】
方法1
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3811
  5. #include<cstdio>
  6. #define N 3000001
  7. long long a[N],f[N],g[N];
  8. int n,mod;
  9. long long ksm(long long a,long long b)
  10. {
  11. long long res=1;
  12. while(b)
  13. {
  14. if(b&1) res=(res*a)%mod;
  15. a=(a*a)%mod;b>>=1;
  16. }
  17. return res;
  18. }
  19. signed main()
  20. {
  21. scanf("%d%d",&n,&mod);
  22. f[0]=1;
  23. for(int i=1;i<=n;i++) f[i]=(f[i-1]*i)%mod;
  24. g[n]=ksm(f[n],mod-2);
  25. for(int i=n;i>=1;i--) g[i-1]=(g[i]*i)%mod;
  26. for(int i=1;i<=n;i++) a[i]=(f[i-1]*g[i])%mod;
  27. for(int i=1;i<=n;i++)
  28. printf("%lld\n",a[i]);
  29. return 0;
  30. }

复杂度,常数略大

方法2
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3811
  5. #include<cstdio>
  6. #define N 3000001
  7. int n,p;
  8. long long a[N];
  9. int main()
  10. {
  11. scanf("%d%d",&n,&p);
  12. a[1]=1;
  13. for(int i=2;i<=n;i++) a[i]=(p-(p/i))*a[p%i]%p;
  14. for(int i=1;i<=n;i++) printf("%lld\n",a[i]);
  15. return 0;
  16. }

复杂度

快速幂

【简介】

顾名思义,快速幂就是快速算底数的次幂。其时间复杂度为 ,与朴素的相比效率有了极大的提高。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1226
  5. #include<cstdio>
  6. long long ksm(long long a,long long b,long long mod)
  7. {
  8. int res=1;
  9. while(b)
  10. {
  11. if(b&1) res=(res*a)%mod;
  12. a=(a*a)%mod;b>>=1;
  13. }
  14. return res%mod;
  15. }
  16. int main()
  17. {
  18. long long a,b,c;
  19. scanf("%lld%lld%lld",&a,&b,&c);
  20. printf("%lld^%lld mod %lld=%lld",a,b,c,ksm(a,b,c));
  21. return 0;
  22. }

复杂度

筛法求积性函数

【简介】

筛法是一种简单检定素数的算法。据说是古希腊的埃拉托斯特尼(Eratosthenes,约公元前274~194年)发明的,又称埃拉托斯特尼筛法(sieve of Eratosthenes),后来扩展到利用筛法求积性函数。

【一些性质】

积性函数:对于函数,若满足对任意互质的数字,那么称函数为积性函数。
狄利克雷卷积:对于函数,定义它们的卷积为
两个积性函数的狄利克雷卷积仍为积性函数。
积性函数都可以用线性筛筛出来

【代码实现】
方法1
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3383#sub
  5. #include<cstdio>
  6. #include<cstring>
  7. #define N 10000001
  8. bool vis[N];
  9. int n;
  10. void prime()
  11. {
  12. vis[0]=vis[1]=false;
  13. for(register int i=2;i<=n;i++)
  14. if(vis[i])
  15. {
  16. for(register int j=i+i;j<=n;j+=i)
  17. vis[j]=false;
  18. }
  19. return;
  20. }
  21. int q,x;
  22. int main()
  23. {
  24. memset(vis,true,sizeof(vis));
  25. scanf("%d%d",&n,&q);
  26. prime();
  27. for(int i=1;i<=q;i++)
  28. {
  29. scanf("%d",&x);
  30. puts(vis[x]?"Yes":"No");
  31. }
  32. return 0;
  33. }

复杂度

方法2
  1. #include<cstdio>
  2. #include<cstring>
  3. #define MAXN 100005
  4. #define MAXL 1299710
  5. int prime[MAXN];
  6. int check[MAXL];
  7. int tot = 0;
  8. memset(check, 0, sizeof(check));
  9. for (int i = 2; i < MAXL; ++i)
  10. {
  11. if (!check[i])
  12. {
  13. prime[tot++] = i;
  14. }
  15. for (int j = 0; j < tot; ++j)
  16. {
  17. if (i * prime[j] > MAXL) break;
  18. check[i*prime[j]] = 1;
  19. if (i % prime[j] == 0) break;
  20. }
  21. }

复杂度

【利用筛法求一些常见的积性函数】

复杂度
下面的isprime数组里,1表示不是素数,0表示是素数,请大家注意

莫比乌斯函数
  1. isprime[1] = 1; mu[1] = 1;
  2. for(int i = 2; i < N; ++i){
  3. if(!isprime[i]){ prime[++num] = i; mu[i] = -1; }
  4. for(int j = 1; j <= num && i * prime[j] < N; ++j){
  5. isprime[i * prime[j]] = 1;
  6. if(i % prime[j]) mu[i * prime[j]] = -mu[i];
  7. else{ mu[i * prime[j]] = 0; break; }
  8. }
  9. }
欧拉函数
  1. isprime[1] = 1; phi[1] = 1;
  2. for(int i = 2; i < N; ++i){
  3. if(!isprime[i]){ prime[++num] = i; phi[i] = i - 1; }
  4. for(int j = 1; j <= num && i * prime[j] < N; ++j){
  5. isprime[i * prime[j]] = 1;
  6. if(i % prime[j]) phi[i*prime[j]] = phi[i] * (prime[j] - 1);
  7. else{ phi[i * prime[j]] = phi[i] * prime[j]; break; }
  8. }
  9. }
约数个数
  1. isprime[1]=1,d[1]=1;
  2. for(int i=2;i<N;++i)
  3. {
  4. if(!isprime[i])
  5. {
  6. prime[++num]=i;
  7. d[i]=2;
  8. pred[i]=1;
  9. }
  10. for(int j=1;j<=num&&i*prime[j]<N;++j)
  11. {
  12. isprime[i*prime[j]]=1;
  13. if(i%prime[j])d[i*prime[j]]=d[i]*d[prime[j]],pred[i*prime[j]]=1;
  14. else
  15. {
  16. pred[i*prime[j]]=pred[i]+1;
  17. d[i*prime[j]]=d[i]/(pred[i]+1)*(pred[i]+2);
  18. break;
  19. }
  20. }
  21. }
约数的和
  1. void Prepare()
  2. {
  3. isprime[1]=1;
  4. f[1]=mu[1]=1;
  5. for(int i=2;i<N;++i)
  6. {
  7. if(!isprime[i])
  8. {
  9. prime[++num]=i;f[i]=i+1;mu[i]=-1;
  10. sumd[i]=1+i;powd[i]=i;
  11. }
  12. for(int j=1;j<=num&&i*prime[j]<N;++j)
  13. {
  14. isprime[i*prime[j]]=1;
  15. if(i%prime[j])
  16. {
  17. sumd[i*prime[j]]=1+prime[j];
  18. powd[i*prime[j]]=prime[j];
  19. f[i*prime[j]]=f[i]*f[prime[j]];
  20. }
  21. else
  22. {
  23. powd[i*prime[j]]=powd[i]*prime[j];
  24. sumd[i*prime[j]]=sumd[i]+powd[i*prime[j]];
  25. f[i*prime[j]]=f[i]/sumd[i]*sumd[i*prime[j]];
  26. break;
  27. }
  28. }
  29. }
  30. }

*中国剩余定理

【简介】

中国剩余定理是中国古代求解一次同余式组的方法。是数论中一个重要定理。又称中国余数定理。一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,叫做“物不知数”问题。
在OI中,中国剩余定理主要解决以下问题:

【代码实现】
  1. #include<cstdio>
  2. #define N 101
  3. int n,m[N],a[N],lcm=1,d;
  4. int exgcd(int a,int b,int &x,int &y)
  5. {
  6. if(!b)
  7. {
  8. x=1,y=0;
  9. return a;
  10. }
  11. int re=exgcd(b,a%b,x,y),tmp=x;
  12. x=y,y=tmp-(a/b)*y;
  13. return re;
  14. }
  15. int work()
  16. {
  17. int x,y,re=0;
  18. for(int i=1;i<=n;i++) lcm=lcm*m[i];
  19. for(int i=1;i<=n;i++)
  20. {
  21. int kl=lcm/m[i];
  22. d=exgcd(kl,m[i],x,y);
  23. x=(x%m[i]+m[i])%m[i];
  24. re=(re+a[i]*x*kl)%lcm;
  25. }
  26. return re;
  27. }
  28. int main()
  29. {
  30. scanf("%d",&n);
  31. for(inti=1;i<=n;i++)scanf("%d%d",&m[i],&a[i]);;
  32. printf("%d",work());
  33. return 0;
  34. }

复杂度

*BSGS

【简介】

大步小步法(Baby-Step-Giant-Step,简称BSGS),可以较高效的求解形如是素数)的同余方程。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P4884
  5. #include<cstdio>
  6. #include<map>
  7. #include<cmath>
  8. using namespace std;
  9. __int128 FastPow(__int128 a,__int128 k,__int128 p)
  10. {
  11. __int128 ans=1;
  12. while(k)
  13. {
  14. if(k&1) (ans*=a)%=p;
  15. (a*=a)%=p;
  16. k>>=1;
  17. }
  18. return ans;
  19. }
  20. __int128 BSGS(__int128 a,__int128 b,__int128 p)
  21. {
  22. map<__int128,__int128>hash;
  23. hash.clear();
  24. b%=p;
  25. __int128 t=(__int128)sqrt((double)p)+1;
  26. for(__int128 j=0;j<t;j++)
  27. {
  28. __int128 val=b*FastPow(a,j,p)%p;
  29. hash[val]=j;
  30. }
  31. a=FastPow(a,t,p);
  32. if(!a)return(b==0)?1:-1;
  33. for(__int128 i=0;i<=t;i++)
  34. {
  35. __int128 val=FastPow(a,i,p);
  36. __int128 j=hash.find(val)==hash.end()?-1:hash[val];
  37. if(j>=0&&i*t-j>=0) return i*t-j;
  38. }
  39. return -1;
  40. }
  41. __int128 k,m;
  42. int main()
  43. {
  44. scanf("%lld%lld",&k,&m);
  45. __int128 ans=BSGS(10,(9*k+1)%m,m);
  46. printf("%lld",ans);
  47. return 0;
  48. }

复杂度

整除分块

【简介】

求解类似于的算式。其中,应可以快速计算前缀和。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<cstdio>
  5. #include<ctime>
  6. long long n,ans;
  7. int main()
  8. {
  9. scanf("%lld",&n);
  10. for(long long l=1,r;l<=n;l=r+1)
  11. {
  12. r=n/(n/l);
  13. ans=(r-l+1)*(n/l);
  14. }
  15. printf("%lld",ans);
  16. return 0;
  17. }

复杂度

*Pollard-Rho算法

【简介】

有一类问题,要求我们将一个正整数,分解为两个非平凡因子的乘积
显然我们需要先检测是否为素数如果是素数将无解,可以使用Miller-Rabin算法来进行测试。
Pollard-Rho是一个非常玄学的方式,用于在的期望时间复杂度内计算合数的某个非平凡因子。事实上算法导论给出的是的某个最小因子,满足互质。但是这些都是期望,未必符合实际。但事实上Pollard-Rho算法在实际环境中运行的相当不错。

【代码实现】
  1. #include <cstdio>
  2. #include <algorithm>
  3. #define rep(i, s, t) for(int i = s; i <= t; ++i)
  4. typedef long long ll;
  5. ll H;
  6. ll pls(ll a, ll b, ll p)
  7. {
  8. ll res = 0;
  9. for(; b; b >>= 1, a = (a+a)%p)
  10. if(b & 1) res = (res+a)%p;
  11. return res%p;
  12. }
  13. ll pow(ll a, ll b, ll p)
  14. {
  15. ll res = 1;
  16. for(; b; b >>= 1, a = pls(a, a, p))
  17. if(b & 1) res = pls(res, a, p)%p;
  18. return res % p;
  19. }
  20. bool M(ll p)
  21. {
  22. ll x[60] = {0}, s = 20;
  23. ll rest = p-1, t = 0;
  24. while(!(rest%2))
  25. {
  26. t ++;
  27. rest >>= 1;
  28. }
  29. while(s--)
  30. {
  31. ll a = rand()%(p-1)+1;
  32. x[0] = pow(a, rest, p);
  33. rep(i, 1, t)
  34. {
  35. x[i] = pow(x[i-1], 2, p)%p;
  36. if(x[i] == 1)
  37. if((x[i-1] != 1) && (x[i-1] != p-1)) return false;
  38. }
  39. if(x[t] ^ 1) return false;
  40. }
  41. return true;
  42. }
  43. ll gcd(ll a, ll b)
  44. {
  45. while(b)
  46. {
  47. ll t = a%b;
  48. a = b;
  49. b = t;
  50. }
  51. return a;
  52. }
  53. ll P(ll p)
  54. {
  55. ll c = rand()%(p-1)+1, x1 = rand()%(p-1)+1, x2 = x1;
  56. for(ll i = 2, k = 2; true; ++i)
  57. {
  58. x1 = (pls(x1, x1, p) + c)%p;
  59. ll G = gcd(p, (x2-x1+p)%p);
  60. if(G > 1 && G < p) return G;
  61. if(x2 == x1) return p;
  62. if(i == k) x2 = x1, k <<= 1;
  63. }
  64. }
  65. void solve(long long n)
  66. {
  67. if(n == 1) return;
  68. if(M(n))
  69. {
  70. H = std::min(H , n);
  71. return;
  72. }
  73. long long p = n;
  74. while(p == n) p = P(p);
  75. solve(p);
  76. solve(n/p);
  77. }
  78. int main()
  79. {
  80. int _;
  81. scanf("%d", &_);
  82. while(_--)
  83. {
  84. ll p;
  85. H = 1LL << 54;
  86. scanf("%lld", &p);
  87. solve(p);
  88. if(H ^ p)
  89. printf("%lld\n", H);
  90. else puts("Prime");
  91. }
  92. return 0;
  93. }

复杂度


图论算法模板

图的遍历和存储

【简介】

图是计算机内部一种常见的数据结构,分为无向图和有向图。
在无向图中,若图中任意一对顶点都是连通的,则称此图是连通图。
在有向图中,若任意一对顶点间存在一条从的路径和从的路径,则称此图是强连通图。
无向图的一个极大连通子图称为该图的一个连通分量。
有向图的一个极大强连通子图称为该图的一个强连通分量。
在图的每条边上加上一个数字作权,也称代价,带权的图称为网。

【代码实现】
邻接矩阵存图与遍历
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3916
  5. #include<cstdio>
  6. #include<vector>
  7. using namespace std;
  8. #define N 1001
  9. bool vis[N];
  10. int n,m,ans[N],g[N][N];
  11. void dfs(int now,int start)
  12. {
  13. vis[now]=true;ans[now]=start;
  14. for(int i=1;i<=n;i++)
  15. if(!vis[i]&&g[now][i]) dfs(i,start);
  16. }
  17. int main()
  18. {
  19. scanf("%d%d",&n,&m);
  20. int x,y;
  21. for(int i=1;i<=m;i++)
  22. {
  23. scanf("%d%d",&x,&y);
  24. g[y][x]=1;
  25. }
  26. for(int i=n;i>=1;i--)
  27. if(!vis[i]) dfs(i,i);
  28. for(int i=1;i<=n;i++)
  29. printf("%d%c",ans[i],i==n?'\n':' ');
  30. return 0;
  31. }

复杂度

*Vector存图与遍历
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3916
  5. #include<cstdio>
  6. #include<vector>
  7. using namespace std;
  8. #define N 100001
  9. vector<int> g[N];
  10. bool vis[N];
  11. int n,m,ans[N];
  12. void dfs(int now,int start)
  13. {
  14. vis[now]=true;ans[now]=start;
  15. for(int i=0;i^g[now].size();i++)
  16. if(!vis[g[now][i]]) dfs(g[now][i],start);
  17. }
  18. int main()
  19. {
  20. scanf("%d%d",&n,&m);
  21. int x,y;
  22. for(int i=1;i<=m;i++)
  23. {
  24. scanf("%d%d",&x,&y);
  25. g[y].push_back(x);
  26. }
  27. for(int i=n;i>=1;i--)
  28. if(!vis[i]) dfs(i,i);
  29. for(int i=1;i<=n;i++)
  30. printf("%d%c",ans[i],i==n?'\n':' ');
  31. return 0;
  32. }

复杂度,常数略大

邻接表存图与遍历
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3916
  5. #include<cstdio>
  6. #define M 100001
  7. #define N 100001
  8. struct Edge{
  9. int nxt,to,val;
  10. }e[M];
  11. int n,m,head[N],tot;
  12. bool vis[N];
  13. void AddEdge(int u,int v)
  14. {
  15. e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot;
  16. }
  17. int ans[N];
  18. void dfs(int now,int start)
  19. {
  20. vis[now]=true;ans[now]=start;
  21. for(int i=head[now];i;i=e[i].nxt)
  22. if(!vis[e[i].to]) dfs(e[i].to,start);
  23. }
  24. int main()
  25. {
  26. scanf("%d%d",&n,&m);
  27. int x,y;
  28. for(int i=1;i<=m;i++)
  29. {
  30. scanf("%d%d",&x,&y);
  31. AddEdge(y,x);
  32. }
  33. for(int i=n;i>=1;i--)
  34. if(!vis[i]) dfs(i,i);
  35. for(int i=1;i<=n;i++)
  36. printf("%d%c",ans[i],i==n?'\n':' ');
  37. return 0;
  38. }

复杂度

一笔画问题

【简介】

如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
  我们定义奇点是指跟这个点相连的边数目有奇数个的点。对于能够一笔画的图,我们有以下两个定理。
   定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
   定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
  两个定理的正确性是显而易见的,既然每条边都要经过一次,那么对于欧拉路,除了起点和终点外,每个点如果进入了一次,显然一定要出去一次,显然是偶点。对于欧拉回路,每个点进入和出去次数一定都是相等的,显然没有奇点。
  求欧拉路的算法很简单,使用深度优先遍历即可。
  根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行DFS。

【代码实现】

  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. #define maxn 101
  5. int g[maxn][maxn];//此图用邻接矩阵存储
  6. int du[maxn];//记录每个点的度,就是相连的边的数目
  7. int circuit[maxn];//用来记录找到的欧拉路的路径
  8. int n,e,circuitpos,i,j,x,y,start;
  9. void find_circuit(int i)//这个点深度优先遍历过程寻找欧拉路
  10. {
  11. int j;
  12. for(j=1;j<=n;j++)
  13. if(g[i][j]==1)//从任意一个与它相连的点出发
  14. {
  15. g[j][i]=g[i][j]=0;
  16. find_circuit(j);
  17. }
  18. circuit[++circuitpos]=i;//记录下路径
  19. }
  20. int main()
  21. {
  22. memset(g,0,sizeof(g));
  23. cin>>n>>e;
  24. for(i=1;i<=e;i++)
  25. {
  26. cin>>x>>y;
  27. g[y][x]=g[x][y]=1;
  28. du[x]++;//统计每个点的度
  29. du[y]++;
  30. }
  31. start=1;//如果有奇点,就从奇点开始寻找,这样找到的就是
  32. for(i=1;i<=n;i++)//欧拉路。没有奇点就从任意点开始,
  33. if(du[i]%2==1)//这样找到的就是欧拉回路。(因为每一个点都是偶点)
  34. start=i;
  35. circuitpos=0;
  36. find_circuit(start);
  37. for(i=1;i<=circuitpos;i++)
  38. cout<<circuit[i]<<" ";
  39. cout<<endl;
  40. return 0;
  41. }

复杂度

哈密顿回路问题

【简介】

欧拉回路是指不重复地走过所有路径的回路,而哈密尔顿环是指不重复地走过所有的点,并且最后还能回到起点的回路。

【代码实现】

  1. #include <queue>
  2. #include <cstdio>
  3. #include <set>
  4. #include <string>
  5. #include <stack>
  6. #include <cmath>
  7. #include <climits>
  8. #include <map>
  9. #include <cstdlib>
  10. #include <iostream>
  11. #include <vector>
  12. #include <algorithm>
  13. #include <cstring>
  14. #define max(a,b) (a>b?a:b)
  15. using namespace std;
  16. typedef long long(LL);
  17. typedef unsigned long long(ULL);
  18. const double eps(1e-8);
  19. char B[1<<15],*S=B,*T=B,ch;
  20. #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
  21. int aa,bb; int F()
  22. {
  23. while(ch=getc(),(ch<'0'||ch>'9')&&ch!='-'); ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
  24. while(ch=getc(),ch>='0'&&ch<='9')aa=aa*10+ch-'0'; return bb?aa:-aa;
  25. }
  26. #define N 100010
  27. int n,swp,cnt,z[N]; long long ans;
  28. #define swap(a,b) (swp=a,a=b,b=swp)
  29. #define abs(x) (x>0?x:-(x))
  30. #define max(a,b) (a>b?a:b)
  31. #define cmax(x) (ans<x?ans=x:1)
  32. struct P {int x,y,id,nx,ny;} p[N];
  33. bool operator<(const P&a,const P&b) {return a.nx<b.nx||a.nx==b.nx&&a.ny<b.ny;}
  34. class Graph
  35. {
  36. private:
  37. int et,la[N],ufs[N],tot;
  38. struct D
  39. {
  40. int x,y,v;
  41. bool operator<(const D&a)const {return v<a.v;}
  42. } d[N<<2];
  43. struct E {int to,v,nxt;} e[N<<1];
  44. int gf(int x) {return ufs[x]==x?x:ufs[x]=gf(ufs[x]);}
  45. void adde(int x,int y,int v)
  46. {
  47. e[++et]=(E) {y,v,la[x]},la[x]=et;
  48. e[++et]=(E) {x,v,la[y]},la[y]=et;
  49. }
  50. public:
  51. Graph() {et=1;}
  52. void add(int x,int y,int v) {d[++tot]=(D) {x,y,v};}
  53. void make()
  54. {
  55. std::sort(d+1,d+1+tot);
  56. for(int i=1; i<=n; i++)ufs[i]=i; cnt=n;
  57. for(int i=1,x,y; i<=tot; i++)
  58. if((x=gf(d[i].x))!=(y=gf(d[i].y)))
  59. {
  60. ufs[x]=y,cnt--,ans+=d[i].v,
  61. adde(d[i].x,d[i].y,d[i].v);
  62. }
  63. }
  64. } G;
  65. struct D {int x,n;} d[N];
  66. bool operator<(const D&a,const D&b) {return a.x<b.x;}
  67. #define dis(i,j) (abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y))
  68. void ins(int i)
  69. {
  70. for(int t=p[i].ny; t<=cnt; t+=t&-t)
  71. if(z[t]==0||p[z[t]].x+p[z[t]].y<p[i].x+p[i].y)z[t]=i;
  72. }
  73. int query(int i)
  74. {
  75. int f=0;
  76. for(int t=p[i].ny; t>0; t-=t&-t)
  77. if(z[t]&&(f==0||p[z[t]].x+p[z[t]].y>p[f].x+p[f].y))f=z[t];
  78. return f;
  79. }
  80. void work()
  81. {
  82. for(int i=1; i<=n; i++)p[i].nx=p[i].x-p[i].y,p[i].ny=p[i].y;
  83. std::sort(p+1,p+1+n);
  84. for(int i=1; i<=n; i++)d[i]=(D) {p[i].ny,i};
  85. std::sort(d+1,d+1+n); d[n+1].x=d[n].x; cnt=1;
  86. for(int i=1; i<=n; i++)
  87. {
  88. p[d[i].n].ny=cnt;
  89. if(d[i].x!=d[i+1].x)cnt++;
  90. }
  91. memset(z,0,sizeof(z));
  92. for(int i=1,j; i<=n; ins(i++))
  93. if(j=query(i))G.add(p[i].id,p[j].id,dis(i,j));
  94. }
  95. int main()
  96. {
  97. n=F();
  98. for(int i=1; i<=n; i++)p[i]=(P) {F(),F(),i}; work();
  99. for(int i=1; i<=n; i++)swap(p[i].x,p[i].y); work();
  100. for(int i=1; i<=n; i++)p[i].y=-p[i].y; work();
  101. for(int i=1; i<=n; i++)swap(p[i].x,p[i].y); work(); G.make();
  102. printf("%lld\n",ans);
  103. }

差分约束系统

【简介】

如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

【代码实现】

  1. //problemID:http://www.poj.org/problem?id=1201
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <queue>
  5. #include <cctype>
  6. #include <algorithm>
  7. using namespace std;
  8. template<class T>
  9. inline void read(T &res) {
  10. char c; res = 0;
  11. while (!(isdigit(c = getchar())));
  12. while (isdigit(c)) res = res * 10 + c - '0', c = getchar();
  13. }
  14. const int N = 50005;
  15. const int INF = 0x3f3f3f3f;
  16. int head[N], vis[N], d[N];
  17. struct edge{
  18. int v, d, next;
  19. edge(int v = 0, int d = 0, int n = 0) : v(v), d(d), next(n){}
  20. }e[N<<2];
  21. int n, ma, mi = INF, k;
  22. queue<int> q;
  23. void add(int u, int v, int d) {
  24. e[k] = edge(v, d, head[u]);
  25. head[u] = k++;
  26. }
  27. int spfa() {
  28. fill(d+mi+1, d+ma+1, -INF);
  29. vis[mi] = 1;
  30. q.push(mi);
  31. while (!q.empty()) {
  32. int t = q.front(); q.pop();
  33. vis[t] = 0;
  34. for (int i = head[t]; i != -1; i = e[i].next) {
  35. int x = e[i].v;
  36. if (d[x] < d[t] + e[i].d) {
  37. d[x] = d[t] + e[i].d;
  38. if (!vis[x]) vis[x] = 1, q.push(x);
  39. }
  40. }
  41. }
  42. return d[ma];
  43. }
  44. int main() {
  45. int k = 0;
  46. memset(head, -1, sizeof(head));
  47. read(n);
  48. for (int i = 0; i < n; i++) {
  49. int a, b, c;
  50. read(a); read(b); read(c);
  51. add(a, b+1, c);
  52. ma = max(ma, b); mi = min(mi, a);
  53. }
  54. ma++;
  55. for (int i = mi; i < ma; i++) add(i, i+1, 0), add(i+1, i, -1);
  56. printf("%d\n", spfa());
  57. return 0;
  58. }

生成树算法

Prim最小生成树算法

【简介】

Prim算法,图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克发现;并在1957年由美国计算机科学家罗伯特·普里姆独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。

【代码实现】
朴素Prim
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3366
  5. #include<cstdio>
  6. #include<algorithm>
  7. #define inf 2147483647
  8. #define maxn 5005
  9. #define maxm 200005
  10. struct edge{
  11. int v,w,next;
  12. }e[maxm<<1];
  13. //注意是无向图,开两倍数组
  14. int head[maxn],dis[maxn],cnt,n,m,tot,now=1,ans;
  15. //已经加入最小生成树的的点到没有加入的点的最短距离
  16. //比如说1和2号节点已经加入了最小生成树,那么dis[3]就等于min(1->3,2->3)
  17. bool vis[maxn];
  18. void add(int u,int v,int w)
  19. {
  20. e[++cnt].v=v;
  21. e[cnt].w=w;
  22. e[cnt].next=head[u];
  23. head[u]=cnt;
  24. }
  25. int prim()
  26. {
  27. //先把dis数组附为极大值
  28. for(int i=2;i<=n;++i)
  29. {
  30. dis[i]=inf;
  31. }
  32. //这里要注意重边,所以要用到min
  33. for(int i=head[1];i;i=e[i].next)
  34. {
  35. dis[e[i].v]=std::min(dis[e[i].v],e[i].w);
  36. }
  37. while(++tot<n)//最小生成树边数等于点数-1
  38. {
  39. int minn=inf;//把minn置为极大值
  40. vis[now]=1;//标记点已经走过
  41. //枚举每一个没有使用的点
  42. //找出最小值作为新边
  43. //注意这里不是枚举now点的所有连边,而是1~n
  44. for(int i=1;i<=n;++i)
  45. {
  46. if(!vis[i]&&minn>dis[i])
  47. {
  48. minn=dis[i];
  49. now=i;
  50. }
  51. }
  52. ans+=minn;
  53. //枚举now的所有连边,更新dis数组
  54. for(int i=head[now];i;i=e[i].next)
  55. {
  56. int v=e[i].v;
  57. if(dis[v]>e[i].w&&!vis[v])
  58. {
  59. dis[v]=e[i].w;
  60. }
  61. }
  62. }
  63. return ans;
  64. }
  65. int main()
  66. {
  67. scanf("%d%d",&n,&m);
  68. for(int i=1,u,v,w;i<=m;++i)
  69. {
  70. scanf("%d%d%d",&u,&v,&w);
  71. add(u,v,w),add(v,u,w);
  72. }
  73. printf("%d",prim());
  74. return 0;
  75. }

复杂度

堆优化Prim
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<cstdio>
  5. #include<cmath>
  6. #include<queue>
  7. #include<vector>
  8. #include<climits>
  9. using namespace std;
  10. #define maxn 99999999
  11. struct E{
  12. int next;
  13. int c;
  14. int zhi;
  15. }e[1005000];
  16. inline int read()
  17. {
  18. char ls=getchar();for (;ls<'0'||ls>'9';ls=getchar());
  19. int x=0;for (;ls>='0'&&ls<='9';ls=getchar()) x=x*10+ls-'0';
  20. return x;
  21. }
  22. int last[100500];
  23. int dis[100500];
  24. int n,m;
  25. int ans;
  26. int k;
  27. struct node{
  28. int num;
  29. node(){}
  30. node(int h){num=h;}
  31. bool operator <(const node & p)const
  32. {
  33. return dis[p.num]<dis[num];
  34. }
  35. };
  36. int located[100500];
  37. int heap[300500];
  38. int heap_size;
  39. inline void put(int d)
  40. {
  41. int now,next;
  42. heap[++heap_size]=d;
  43. now=heap_size;
  44. located[d]=now;
  45. while(now>1)
  46. {
  47. next=now>>1;
  48. if(dis[heap[now]]>=dis[heap[next]])break;
  49. located[d]=next;
  50. located[heap[next]]=now;
  51. swap(heap[now],heap[next]);
  52. now=next;
  53. }
  54. return;
  55. }
  56. inline void change(int d)
  57. {
  58. int now,next;
  59. now=located[d];
  60. while(now>1)
  61. {
  62. next=now>>1;
  63. if(dis[heap[now]]>=dis[heap[next]])break;
  64. located[d]=next;
  65. located[heap[next]]=now;
  66. swap(heap[now],heap[next]);
  67. now=next;
  68. }
  69. return;
  70. }
  71. inline int get()
  72. {
  73. int now,next,res;
  74. res=heap[1];
  75. heap[1]=heap[heap_size--];
  76. now=1;
  77. located[heap[1]]=1;
  78. located[res]=0;
  79. while(now*2<=heap_size)
  80. {
  81. next=now*2;
  82. if(next<heap_size&&dis[heap[next+1]]<dis[heap[next]])++next;
  83. if(dis[heap[now]]<=dis[heap[next]])break;
  84. located[heap[now]]=next;
  85. located[heap[next]]=now;
  86. swap(heap[next],heap[now]);
  87. now=next;
  88. }
  89. return res;
  90. }
  91. int main()
  92. {
  93. n=read(),m=read();
  94. for(int i=1;i<=m;i++)
  95. {
  96. int a1,b1,c1;
  97. a1=read();b1=read();c1=read();
  98. e[++k].next=b1;
  99. e[k].c=c1;
  100. e[k].zhi=last[a1];
  101. last[a1]=k;
  102. e[++k].next=a1;
  103. e[k].c=c1;
  104. e[k].zhi=last[b1];
  105. last[b1]=k;
  106. }
  107. for(int i=2;i<=n;i++)
  108. dis[i]=maxn;
  109. for(int j=last[1];j;j=e[j].zhi)
  110. {
  111. int y=e[j].next;
  112. int c=e[j].c;
  113. if(c<dis[y])dis[y]=c;
  114. }
  115. for(int i=2;i<=n;++i)
  116. put(i);
  117. for(int i=1;i<=n-1;++i)
  118. {
  119. int x=get();ans+=dis[x];
  120. for(int j=last[x];j;j=e[j].zhi)
  121. {
  122. int y=e[j].next;
  123. int c=e[j].c;
  124. if(c<dis[y])
  125. {
  126. dis[y]=c;
  127. change(y);
  128. }
  129. }
  130. }
  131. cout<<ans<<endl;
  132. return 0;
  133. }

复杂度

Kruskal最小生成树算法

【简介】

求加权连通图的最小生成树的算法。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3366
  5. #include<cstdio>
  6. #include<cctype>
  7. #include<algorithm>
  8. inline char getcha(){
  9. static char buf[100000],*p1=buf,*p2=buf;
  10. return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
  11. }
  12. int readin()
  13. {
  14. char ch=getcha();int sum=0;
  15. while(!(ch>='0'&&ch<='9'))ch=getcha();
  16. while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getcha();
  17. return sum;
  18. }
  19. int n,m,fa[5001],tot;
  20. struct EDGE{
  21. int x,y,val;
  22. }e[200001];
  23. bool cmp(EDGE A,EDGE B)
  24. {
  25. return A.val<B.val;
  26. }
  27. int Find(int x)
  28. {
  29. return ((x==fa[x])?(fa[x]):(fa[x]=Find(fa[x])));
  30. }
  31. long long ans=0;
  32. int main()
  33. {
  34. n=readin();m=readin();
  35. for(int i=1;i<=n;i++) fa[i]=i;
  36. for(int i=1;i<=m;i++)
  37. {
  38. e[i].x=readin();
  39. e[i].y=readin();
  40. e[i].val=readin();
  41. }
  42. std::sort(e+1,e+m+1,cmp);
  43. int Root1,Root2;
  44. for(int i=1;i<=m;i++)
  45. {
  46. Root1=Find(e[i].x);Root2=Find(e[i].y);
  47. if(Root1!=Root2)
  48. {
  49. fa[Root1]=Root2;
  50. tot++;
  51. ans+=e[i].val;
  52. }
  53. if(tot==n-1)
  54. {
  55. printf("%lld",ans);
  56. return 0;
  57. }
  58. }
  59. puts("orz");
  60. return 0;
  61. }

复杂度

*严格次小生成树算法

【代码实现】
  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. #define maxm 300001
  5. #define inf 2147483647
  6. #define maxn 100001
  7. struct Edge{
  8. int u,v,w,next;
  9. }e[maxm<<1];
  10. struct qj{
  11. int ma,ma2;
  12. }q[maxn<<2];
  13. struct Edge1{
  14. int u,v,w;
  15. bool operator < (const Edge1 &x)const
  16. {
  17. return w<x.w;//按照边权排序
  18. }
  19. }edge[maxm];
  20. int n,m,vis[maxm],ans=inf,head[maxn],cnt,fa[maxn],mtree;
  21. void add(int u,int v,int w)
  22. {
  23. e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt;
  24. }//前向星加边
  25. namespace smallesttree
  26. {
  27. int find(int x)
  28. {
  29. while(x!=fa[x]) x=fa[x]=fa[fa[x]];
  30. return x;
  31. }//并查集找祖先
  32. void init()
  33. {
  34. for(int i=1;i<=n;i++) fa[i]=i;//预处理并查集
  35. for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
  36. }
  37. void kruskal()
  38. {
  39. init();
  40. sort(edge,edge+m);
  41. int T=0;
  42. for(int i=0;i<m;++i)
  43. {
  44. int eu=find(edge[i].u),ev=find(edge[i].v);//寻找祖先
  45. if(eu!=ev)
  46. {
  47. add(edge[i].u,edge[i].v,edge[i].w),add(edge[i].v,edge[i].u,edge[i].w);
  48. mtree+=edge[i].w;//记录子树大小
  49. fa[ev]=eu;//合并
  50. vis[i]=1;//标记该边为树边
  51. if(++T==n-1)break;//边数等于节点数+1即为一颗树
  52. }
  53. }
  54. }
  55. }
  56. //求出最小生成树
  57. namespace treecut
  58. {
  59. int dep[maxn],father[maxn],top[maxn],W[maxn],a[maxn],size[maxn],son[maxn],seg[maxn],col;
  60. //dep:深度father:父亲节点top:重链的顶端W:到根节点的距离a:点的权值
  61. //size:子树大小son:重儿子seg:在线段树中的序号(dfs序)
  62. void dfs1(int u,int fr)
  63. {
  64. dep[u]=dep[fr]+1;
  65. size[u]=1;
  66. father[u]=fr;
  67. for(int i=head[u];i;i=e[i].next)
  68. {
  69. int v=e[i].v;
  70. if(v!=fr)
  71. {
  72. W[v]=W[u]+e[i].w;//W为每一个点到根节点的距离
  73. dfs1(v,u);
  74. size[u]+=size[v];
  75. if(size[v]>size[son[u]])son[u]=v;
  76. }
  77. }
  78. }//预处理出dep、size、father以及son
  79. void dfs2(int now,int fi)
  80. {
  81. top[now]=fi;
  82. seg[now]=++col;
  83. a[col]=W[now]-W[father[now]];//a为点的权值(它与之父亲节点边的权值)(相当于前缀和)
  84. if(!son[now])return;
  85. dfs2(son[now],fi);
  86. for(int i=head[now];i;i=e[i].next)
  87. {
  88. int v=e[i].v;
  89. if(v!=son[now]&&v!=father[now])dfs2(v,v);
  90. }
  91. }//预处理出每个节点的top、seg以及权值
  92. //树剖模板就不解释了
  93. #define ls k<<1
  94. #define rs k<<1|1
  95. bool CMP(int a,int b){return a>b;}
  96. int getse(int x,int g,int z,int c)
  97. {
  98. int a[4]={x,g,z,c};
  99. sort(a,a+4,CMP);
  100. for(int i=1;i<3;++i)
  101. if(a[i]!=a[0]) return a[i];
  102. return 0;
  103. }
  104. //找到两个区间的最大值和严格次大值(四个数)的最大值与严格次大值
  105. //就是合并两个区间的最大值和严格次大值
  106. void build(int k,int l,int r)
  107. {
  108. if(l==r)
  109. {
  110. q[k].ma=a[l];
  111. return;
  112. }
  113. int mid=(l+r)>>1;
  114. build(ls,l,mid),build(rs,mid+1,r);
  115. q[k].ma=max(q[ls].ma,q[rs].ma);
  116. q[k].ma2=getse(q[ls].ma,q[rs].ma,q[ls].ma2,q[rs].ma2);
  117. }//预处理出区间最大值与次大值
  118. qj query(int k,int l,int r,int ll,int rr)
  119. {
  120. if(ll>r||rr<l)return(qj){-inf,-inf};
  121. if(ll<=l&&rr>=r)return(qj){q[k].ma,q[k].ma2};
  122. int mid=(l+r)>>1;
  123. qj t1=query(ls,l,mid,ll,rr),t2=query(rs,mid+1,r,ll,rr);
  124. return(qj){max(t1.ma,t2.ma),getse(t1.ma,t2.ma,t1.ma2,t2.ma2)};
  125. }//查询区间的区间的最大值与次小值
  126. int LCA(int u,int v,int d)
  127. {
  128. int need=-inf;
  129. while(top[u]!=top[v])
  130. {
  131. if(dep[top[u]]<dep[top[v]])swap(u,v);
  132. qj temp=query(1,1,n,seg[top[u]],seg[u]);
  133. u=father[top[u]];
  134. need=max(need,(temp.ma==d)?temp.ma2:temp.ma);//严格次小边(如果temp.ma==k就是非严格次小)
  135. }
  136. if(dep[u]<dep[v])swap(u,v);//找到LCA
  137. qj temp=query(1,1,n,seg[v]+1,seg[u]);
  138. return max(need,(temp.ma==d)?temp.ma2:temp.ma);//同上
  139. }
  140. void init()
  141. {
  142. dfs1(1,0),dfs2(1,1),build(1,1,n);
  143. }
  144. }
  145. //树链剖分
  146. int main()
  147. {
  148. scanf("%d%d",&n,&m);
  149. smallesttree::kruskal();//求出最小生成树
  150. treecut::init();//预处理
  151. for(int i=0;i<m;++i)
  152. {
  153. if(vis[i])continue;//枚举所有非树边(没有在最小生成树的边)
  154. int temp=mtree+edge[i].w-treecut::LCA(edge[i].u,edge[i].v,edge[i].w);
  155. if(ans>temp&&temp!=mtree+e[i].w&&temp>mtree)ans=temp;
  156. }
  157. printf("%d",ans);
  158. return 0;
  159. }

复杂度

生成树计数算法

【代码实现】
  1. #include<iostream>
  2. #include<math.h>
  3. #include<stdio.h>
  4. #include<string.h>
  5. using namespace std;
  6. #define zero(x) ((x>0? x:-x)<1e-15)
  7. int const MAXN = 100;
  8. double a[MAXN][MAXN];
  9. double b[MAXN][MAXN];
  10. int g[53][53];
  11. int n,m;
  12. double det(double a[MAXN][MAXN],int n)
  13. {
  14. int i, j, k, sign = 0;
  15. double ret = 1, t;
  16. for (i = 0; i < n; i++)
  17. for (j = 0; j < n; j++)
  18. b[i][j] = a[i][j];
  19. for (i = 0; i < n; i++)
  20. {
  21. if (zero(b[i][i]))
  22. {
  23. for (j = i + 1; j < n; j++)
  24. if (!zero(b[j][i]))
  25. break;
  26. if (j == n)
  27. return 0;
  28. for (k = i; k < n; k++)
  29. t = b[i][k], b[i][k] = b[j][k], b[j][k] = t;
  30. sign++;
  31. }
  32. ret *= b[i][i];
  33. for (k = i + 1; k < n; k++)
  34. b[i][k] /= b[i][i];
  35. for (j = i + 1; j < n; j++)
  36. for (k = i + 1; k < n; k++)
  37. b[j][k] -= b[j][i] * b[i][k];
  38. }
  39. if (sign & 1)
  40. ret = -ret;
  41. return ret;
  42. }
  43. void build()
  44. {
  45. while (m--)
  46. {
  47. int a, b;
  48. scanf("%d%d", &a, &b);
  49. g[a-1][b-1]=g[b-1][a-1]=1;
  50. }
  51. }
  52. int main()
  53. {
  54. int cas;
  55. scanf("%d", &cas);
  56. while (cas--)
  57. {
  58. scanf("%d%d", &n, &m);
  59. memset(g,0,sizeof(g));
  60. build();
  61. memset(a,0,sizeof(a));
  62. for (int i=0; i<n; i++)
  63. {
  64. int d=0;
  65. for (int j=0; j<n; j++)
  66. if (g[i][j])
  67. d++;
  68. a[i][i]=d;
  69. }
  70. for (int i=0; i<n; i++)
  71. for (int j=0; j<n; j++)
  72. if (g[i][j])
  73. a[i][j]=-1;
  74. double ans = det(a, n-1);
  75. printf("%0.0lf\n", ans);
  76. }
  77. return 0;
  78. }

复杂度

最短路算法

Floyd多源最短路算法

【简介】

Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/2602/
  5. #include<cstdio>
  6. #include<cmath>
  7. #include<cstring>
  8. struct Node{
  9. double x,y;
  10. }a[101];
  11. double f[101][101];
  12. int n,m;
  13. double calc(int b,int c)
  14. {
  15. return sqrt((a[b].x-a[c].x)*(a[b].x-a[c].x)+(a[b].y-a[c].y)*(a[b].y-a[c].y));
  16. }
  17. int main()
  18. {
  19. memset(f,127,sizeof(f));
  20. scanf("%d",&n);
  21. for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
  22. scanf("%d",&m);
  23. int b,c;
  24. for(int i=1;i<=m;i++)
  25. {
  26. scanf("%d%d",&b,&c);
  27. f[b][c]=f[c][b]=calc(b,c);
  28. }
  29. for(int k=1;k<=n;k++)
  30. for(int i=1;i<=n;i++)
  31. for(int j=1;j<=n;j++)
  32. (f[i][k]+f[k][j]<f[i][j])&&(f[i][j]=f[i][k]+f[k][j]);
  33. scanf("%d%d",&b,&c);
  34. printf("%.2lf",f[b][c]);
  35. return 0;
  36. }

复杂度

Dijkstra单源最短路算法

【简介】

迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

【代码实现】
朴素Dijkstra
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<iostream>
  5. #define inf 2147483647
  6. using namespace std;
  7. int e[1001][1001],dis[1001],visit[1001];
  8. int n,m;
  9. int main()
  10. {
  11. int t1,t2,t3;
  12. cin>>n>>m;
  13. for(int i=1;i<=n;i++)
  14. for(int j=1;j<=n;j++)
  15. (i==j)?(e[i][j]=0):(e[i][j]=inf);
  16. for(int i=1;i<=m;i++) cin>>t1>>t2>>t3,e[t1][t2]=t3;
  17. //初始化dis数组,1号顶点到其余各个顶点的初始距离
  18. for(int i=1;i<=n;i++) dis[i]=e[1][i];
  19. //初始化visit数组
  20. for(int i=1;i<=n;i++) visit[i]=0;
  21. visit[1]=1;
  22. int u,min;
  23. for(int i=1;i<=n-1;i++)
  24. {
  25. //找离1号顶点最近的顶点
  26. min=inf;
  27. for(int j=1;j<=n;j++) if(visit[j]==0&&dis[j]<min) min=dis[j],u=j;
  28. visit[u]=1;
  29. for(int v=1;v<=n;v++)
  30. {
  31. if(e[u][v]<inf)
  32. {
  33. if(visit[v]==0&&dis[v]>dis[u]+e[u][v]) dis[v]=dis[u]+e[u][v];
  34. }
  35. }
  36. }
  37. for(int i=1;i<=n;i++) cout<<dis[i]<<endl;
  38. return 0;
  39. }

复杂度

堆优化Dijkstra
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P4779
  5. #include<cstdio>
  6. #include<queue>
  7. using namespace std;
  8. #define N 100001
  9. #define M 200001
  10. struct Edge{
  11. int nxt,to,val;
  12. }e[M<<1];
  13. int n,m,tot,head[N],dis[N],S;
  14. bool vis[N];
  15. priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
  16. void AddEdge(int u,int v,int w)
  17. {
  18. e[++tot].to=v;e[tot].val=w;e[tot].nxt=head[u];head[u]=tot;
  19. }
  20. void Dijkstra()
  21. {
  22. dis[S]=0;
  23. q.push(make_pair(0,S));
  24. while(!q.empty())
  25. {
  26. int x=q.top().second;
  27. q.pop();
  28. if(vis[x]) continue;
  29. vis[x]=true;
  30. for(int i=head[x];i;i=e[i].nxt)
  31. if(dis[e[i].to]>dis[x]+e[i].val)
  32. {
  33. dis[e[i].to]=dis[x]+e[i].val;
  34. q.push(make_pair(dis[e[i].to],e[i].to));
  35. }
  36. }
  37. }
  38. int main()
  39. {
  40. scanf("%d%d%d",&n,&m,&S);
  41. for(int i=1;i<=n;i++) dis[i]=1e9;
  42. int x,y,z;
  43. for(int i=1;i<=m;i++)
  44. {
  45. scanf("%d%d%d",&x,&y,&z);
  46. AddEdge(x,y,z);
  47. }
  48. Dijkstra();
  49. for(int i=1;i<=n;i++) printf("%d%c",dis[i],i==n?'\n':' ');
  50. }

复杂度

Bellman-Ford单源最短路算法

【简介】

Bellman-Ford算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P4779
  5. #include<cstdio>
  6. #include<cstring>
  7. #include<algorithm>
  8. int dis[10010];
  9. int origin[10010],destination[10010],value[10010],S;
  10. int n,m;
  11. int main()
  12. {
  13. scanf("%d%d%d",&n,&m,&S);
  14. for(int i=1;i<=m;i++)
  15. scanf("%d%d%d",&origin[i],&destination[i],&value[i]);
  16. memset(dis,0x7f,sizeof(dis));
  17. dis[1]=0;
  18. for(int i=1;i<=n-1;i++)
  19. for(int j=1;j<=m;j++)
  20. dis[destination[j]]=std::min(dis[destination[j]],dis[origin[j]]+value[j]);
  21. for(int i=1;i<=n;i++) printf("%d ",dis[i]);
  22. return 0;
  23. }

复杂度

Bellman-Ford算法的队列优化(SPFA单源最短路算法)

【简介】

SPFA 算法是 Bellman-Ford算法的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3371
  5. #include<cstdio>
  6. #include<vector>
  7. using namespace std;
  8. const int maxn=10001,inf=1000000000;
  9. struct node{
  10. int to,z;
  11. };
  12. int p[maxn],d[maxn];
  13. vector<node>e[maxn];
  14. int q[maxn*100];
  15. int main()
  16. {
  17. int m,n,s;
  18. scanf("%d%d%d",&n,&m,&s);
  19. for(int i=1;i<=m;i++)
  20. {
  21. int x,y,z;
  22. scanf("%d%d%d",&x,&y,&z);
  23. node t;
  24. t.to=y;
  25. t.z=z;
  26. e[x].push_back(t);
  27. }
  28. for(int i=1;i<=n;i++)d[i]=inf;
  29. d[s]=0,q[1]=s,p[s]=1;
  30. int f=0,l=1;
  31. while(f<l)
  32. {
  33. f++;
  34. int x=q[f];
  35. for(int i=0;i<e[x].size();i++)
  36. {
  37. int u=e[x][i].to,v=e[x][i].z;
  38. if(d[u]>d[x]+v)
  39. {
  40. d[u]=d[x]+v;
  41. if(!p[u]) q[++l]=u,p[u]=1;
  42. }
  43. }
  44. p[x]=0;
  45. }
  46. for(int i=1;i<=n;i++) printf("%d ",d[i]<inf?d[i]:2147483647);
  47. return 0;
  48. }

SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为

*Johnson多源最短路算法

【简介】

Johnson算法可以求解每对顶点之间的最短路径。对于稀疏图,该算法在要好于Floyd算法。算法与Floyd算法类似,每对顶点之间的最短距离用二维数组D表示;如果图中存在负环,算法将输出警告信息。Johnson算法把Bellman-Ford算法和Dijkstra算法作为其子函数。

【代码实现】
  1. public class JohnsonAlgo {
  2. double D[][];
  3. int P[][];
  4. GraphLnk G;
  5. /**
  6. * 构造函数
  7. */
  8. public JohnsonAlgo(GraphLnk G) {
  9. this.G = G;
  10. D = new double[G.get_nv()][G.get_nv()];
  11. P = new int[G.get_nv()][G.get_nv()];
  12. }
  13. public boolean Johnson(){
  14. // 创建一个图_G
  15. GraphLnk _G = new GraphLnk(G.get_nv() + 1);
  16. for(int i = 0; i < G.get_nv(); i++){
  17. for(Edge e = G.firstEdge(i);
  18. G.isEdge(e); e = G.nextEdge(e))
  19. _G.setEdgeWt(e.get_v1(), e.get_v2(), G.getEdgeWt(e));
  20. }
  21. // 在原图的基础上添加一个顶点ad
  22. int ad = _G.get_nv() - 1;
  23. for(int i = 0; i < G.get_nv(); i++){
  24. _G.setEdgeWt(ad, i, 0);
  25. }
  26. // 首先调用Bellman-Ford算法,以ad为起始点
  27. MinusWeightGraph swg = new MinusWeightGraph(_G);
  28. swg.BellmanFord(ad);
  29. // h函数
  30. int h[] = new int[G.get_nv() + 1];
  31. System.out.println("Bellman-Ford算法结果:");
  32. for(int i = 0; i < _G.get_nv(); i++)
  33. System.out.print((h[i] = (int)swg.D[i]) + "\t");
  34. System.out.println();
  35. for(int i = 0; i < _G.get_nv() - 1; i++)
  36. for(Edge e = G.firstEdge(i);
  37. G.isEdge(e); e = G.nextEdge(e))
  38. // 检测有没有负环
  39. if(h[e.get_v2()] > h[e.get_v1()] + _G.getEdgeWt(e))
  40. {
  41. System.out.println("图中有负环。");
  42. return false;
  43. }
  44. // 如果没有则重赋权
  45. else{
  46. int u = G.edge_v1(e), v = G.edge_v2(e);
  47. int wt = (int) (G.getEdgeWt(e) +
  48. h[G.edge_v1(e)] - h[G.edge_v2(e)]);
  49. G.setEdgeWt(u, v, wt);
  50. }
  51. System.out.println("重赋权后的各条边的权值:");
  52. for(int u = 0; u < G.get_nv(); u++){
  53. for(Edge e = G.firstEdge(u);
  54. G.isEdge(e);
  55. e = G.nextEdge(e)){
  56. System.out.print(u + "-" + e.get_v2() +
  57. " " + G.getEdgeWt(e) + "\t");
  58. }
  59. System.out.println();
  60. }
  61. // Dijkstra 算法求解每一个顶点的最短路径树
  62. SingleSourceShortestPaths sssp = new SingleSourceShortestPaths(G);
  63. for(int i = 0; i < G.get_nv(); i++){
  64. sssp.Dijkstra(i);
  65. System.out.println("\n第" + i + "顶点Dijkstra结果:");
  66. for(int j = 0; j < G.get_nv(); j++){
  67. System.out.print(sssp.D[j] + "\t");
  68. D[i][j] = sssp.D[j] + h[j] - h[i];
  69. P[i][j] = sssp.V[j];
  70. }
  71. System.out.println();
  72. }
  73. return true;
  74. }
  75. }

复杂度

Dijkstra次短路算法

【代码实现】
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <queue>
  4. #include <vector>
  5. #include <algorithm>
  6. using namespace std;
  7. const int maxn = 1000 + 5;
  8. const int INF = 0x3f3f3f3f;
  9. struct Node {
  10. int v, c, flag;
  11. Node (int _v = 0, int _c = 0, int _flag = 0) : v(_v), c(_c), flag(_flag) {}
  12. bool operator < (const Node &rhs) const {
  13. return c > rhs.c;
  14. }
  15. };
  16. struct Edge {
  17. int v, cost;
  18. Edge (int _v = 0, int _cost = 0) : v(_v), cost(_cost) {}
  19. };
  20. vector<Edge>E[maxn];
  21. bool vis[maxn][2];
  22. int dist[maxn][2];
  23. void Dijkstra(int n, int s) {
  24. memset(vis, false, sizeof(vis));
  25. for (int i = 1; i <= n; i++) {
  26. dist[i][0] = INF;
  27. dist[i][1] = INF;
  28. }
  29. priority_queue<Node>que;
  30. dist[s][0] = 0;
  31. que.push(Node(s, 0, 0));
  32. while (!que.empty()) {
  33. Node tep = que.top(); que.pop();
  34. int u = tep.v;
  35. int flag = tep.flag;
  36. if (vis[u][flag]) continue;
  37. vis[u][flag] = true;
  38. for (int i = 0; i < (int)E[u].size(); i++) {
  39. int v = E[u][i].v;
  40. int cost = E[u][i].cost;
  41. if (!vis[v][0] && dist[v][0] > dist[u][flag] + cost) {
  42. dist[v][1] = dist[v][0];
  43. dist[v][0] = dist[u][flag] + cost;
  44. que.push(Node(v, dist[v][0], 0));
  45. que.push(Node(v, dist[v][1], 1));
  46. } else if (!vis[v][1] && dist[v][1] > dist[u][flag] + cost) {
  47. dist[v][1] = dist[u][flag] + cost;
  48. que.push(Node(v, dist[v][1], 1));
  49. }
  50. }
  51. }
  52. }
  53. void addedge(int u, int v, int w) {
  54. E[u].push_back(Edge(v, w));
  55. }
  56. int main() {
  57. int n, m, v, w;
  58. while (scanf("%d", &n) != EOF) {
  59. for (int i = 0; i <= n; i++) E[i].clear();
  60. for (int u = 1; u <= n; u++) {
  61. scanf("%d", &m);
  62. for (int j = 0; j < m; j++) {
  63. scanf("%d%d", &v, &w);
  64. addedge(u, v, w);
  65. }
  66. }
  67. Dijkstra(n, 1);
  68. printf("%d\n", dist[n][1]);
  69. }
  70. return 0;
  71. }

复杂度

K短路算法

【代码实现】
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxd=5010;
  4. int dis[maxd];
  5. bool vis[maxd];
  6. int n,m,e;
  7. struct sd {
  8. int num, len;
  9. bool operator < (const sd &other) const {
  10. return len > other.len;
  11. }
  12. };
  13. vector<sd> edge[maxd],redge[maxd];
  14. void spfa()
  15. {
  16. memset(dis,127,sizeof(dis));
  17. dis[e]=0;
  18. queue<int> q;
  19. q.push(e);
  20. vis[e]=true;
  21. while(!q.empty())
  22. {
  23. int now=q.front(); q.pop(); vis[now]=false;
  24. for(int i=redge[now].size()-1;i>=0;--i)
  25. {
  26. if(dis[redge[now][i].num]>dis[now]+redge[now][i].len)
  27. dis[redge[now][i].num]=dis[now]+redge[now][i].len;
  28. if(!vis[redge[now][i].num])
  29. {
  30. vis[redge[now][i].num]=true;
  31. q.push(redge[now][i].num);
  32. }
  33. }
  34. }
  35. }
  36. int A_star(int cnt)
  37. {
  38. int ccnt=0;
  39. priority_queue<sd> q;
  40. q.push((sd){1,dis[1]});//important
  41. while(!q.empty())
  42. {
  43. sd now=q.top();
  44. q.pop();
  45. if(now.num==e)
  46. {
  47. ccnt++;
  48. if(cnt==ccnt)
  49. {
  50. return now.len;
  51. }
  52. continue;//important
  53. }
  54. for(int i=edge[now.num].size()-1;i>=0;--i)
  55. {
  56. q.push((sd){edge[now.num][i].num,now.len-dis[now.num]+edge[now.num][i].len+dis[edge[now.num][i].num]});//important
  57. }
  58. }
  59. }
  60. int main()
  61. {
  62. int us;
  63. scanf("%d%d%d",&n,&m,&e);
  64. scanf("%d",&us);
  65. int a,b,c;
  66. for(int i=1;i<=m;++i)
  67. {
  68. scanf("%d%d%d",&a,&b,&c);
  69. edge[a].push_back((sd){b,c});
  70. redge[b].push_back((sd){a,c});
  71. }
  72. spfa();
  73. if(us==1) goto flag;
  74. cout<<A_star(us); return 0;
  75. flag:
  76. printf("%d",dis[1]);
  77. return 0;
  78. }

LCA(最近公共祖先)算法

倍增法求LCA

【简介】

利用二进制的思想,想办法使一步一步向上搜索变成以的向上跳。需要定义一个数组,使表示节点倍祖先。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3379
  5. #include<cstdio>
  6. #include<algorithm>
  7. #define N 500001
  8. struct Edge{
  9. int nxt,to;
  10. }e[N<<1];
  11. int n,tot,q,root,f[N][20],deep[N],head[N];
  12. void AddEdge(int u,int v)
  13. {
  14. e[++tot].to=v;e[tot].nxt=head[u];head[u]=tot;
  15. }
  16. void dfs(int now)
  17. {
  18. for(int i=1;i<=19;i++)
  19. f[now][i]=f[f[now][i-1]][i-1];
  20. for(int i=head[now];i;i=e[i].nxt)
  21. if(!deep[e[i].to])
  22. {
  23. deep[e[i].to]=deep[now]+1;
  24. f[e[i].to][0]=now;
  25. dfs(e[i].to);
  26. }
  27. }
  28. int LCA(int x,int y)
  29. {
  30. if(deep[x]<deep[y]) std::swap(x,y);
  31. for(int i=19;i>=0;i--)
  32. if(deep[f[x][i]]>deep[y]) x=f[x][i];
  33. if(deep[x]>deep[y]) x=f[x][0];
  34. if(x==y) return x;
  35. for(int i=19;i>=0;i--)
  36. if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
  37. return f[x][0];
  38. }
  39. int main()
  40. {
  41. scanf("%d%d%d",&n,&q,&root);
  42. deep[root]=1;
  43. int x,y;
  44. for(int i=1;i^n;i++)
  45. {
  46. scanf("%d%d",&x,&y);
  47. AddEdge(x,y),AddEdge(y,x);
  48. }
  49. dfs(root);
  50. for(int i=1;i<=q;i++)
  51. {
  52. scanf("%d%d",&x,&y);
  53. printf("%d\n",LCA(x,y));
  54. }
  55. return 0;
  56. }

复杂度

树链剖分求LCA

【简介】

树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构来维护每一条链。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3379
  5. #include<cstring>
  6. #include<iostream>
  7. #include<cctype>
  8. #include<cstdio>
  9. #include<algorithm>
  10. #define writ(x,c) write(x),putchar(c);
  11. using namespace std;
  12. inline char nc()
  13. {
  14. static char buf[100000],*p1=buf,*p2=buf;
  15. return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
  16. }
  17. inline int read()
  18. {
  19. char c=0;int x=0;bool f=0;
  20. for(;!isdigit(c);c=nc()) if(c=='-') f=1;
  21. for(;isdigit(c);c=nc()) x=(x<<1)+(x<<3)+c-48;
  22. return (f ? -x : x);
  23. }
  24. void write(int x)
  25. {
  26. if(x<0) putchar('-'),x=-x;
  27. if(x>9) write(x/10);
  28. putchar(x%10+'0');
  29. }
  30. const int N=5e5+1;
  31. struct edge{
  32. int v,next;
  33. }e[N<<1];
  34. int n,m,root,tot,head[N],dep[N],siz[N],son[N],top[N],f[N];
  35. void add(int u,int v)
  36. {
  37. e[++tot].v=v,e[tot].next=head[u],head[u]=tot;
  38. }
  39. void dfs1(int x)
  40. {
  41. siz[x]=1,dep[x]=dep[f[x]]+1;
  42. for(int i=head[x];i;i=e[i].next)
  43. {
  44. if(e[i].v==f[x])continue;
  45. f[e[i].v]=x;dfs1(e[i].v);
  46. siz[x]+=siz[e[i].v];
  47. if(!son[x] || siz[son[x]]<siz[e[i].v])
  48. son[x]=e[i].v;
  49. }
  50. }
  51. void dfs2(int x,int tv)
  52. {
  53. top[x]=tv;
  54. if(son[x]) dfs2(son[x],tv);
  55. for(int i=head[x];i;i=e[i].next)
  56. if(e[i].v!=f[x] && e[i].v!=son[x])
  57. dfs2(e[i].v,e[i].v);
  58. }
  59. int main()
  60. {
  61. n=read(),m=read(),root=read();
  62. register int x,y;
  63. for(int i=1;i<n;++i)
  64. {
  65. x=read(),y=read();
  66. add(x,y);add(y,x);
  67. }
  68. dfs1(root);dfs2(root,root);
  69. for(int i=1;i<=m;++i)
  70. {
  71. x=read(),y=read();
  72. while(top[x]!=top[y])
  73. {
  74. if(dep[top[x]]>=dep[top[y]])x=f[top[x]];
  75. else y=f[top[y]];
  76. }
  77. writ(dep[x]<dep[y] ? x : y,'\n');
  78. }
  79. }

复杂度

Tarjan求LCA

【简介】

这种算法是基于DFS和并查集来实现的。设的父亲,节点到根节点的距离。首先从一号根节点(记为)开始访问他的每一个子节点(记为),并用根节点与当前访问的子节点的距离更新值,即,其中表示的距离,然后将当前子节点当做根点用上述同样步骤递归下去,并在递归回溯后将其值更新,这样的目的是保证子节点的所有子树全部被访问过。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3379
  5. #include<iostream>
  6. #include<cstdio>
  7. #include<cstdlib>
  8. #include<cstring>
  9. #include<string>
  10. #include<cmath>
  11. #include<algorithm>
  12. #include<queue>
  13. #include<vector>
  14. #include<cctype>
  15. #define add_qedge(x,y) {ql[++qnum]={i,y,qt[x]};qt[x]=qnum;}
  16. #define top 20000000
  17. #define add_edge(x,y) {l[++num]={y,t[x]};t[x]=num;}
  18. #define max_n 500001
  19. using namespace std;
  20. int f[max_n],t[max_n],qt[max_n],lca[max_n];
  21. bool b[max_n];
  22. int i,num,qnum;
  23. char ch[top];int now_r,now_w=-1;
  24. inline void read(int &x)
  25. {
  26. while (ch[now_r]<48) ++now_r;
  27. for (x=ch[now_r]-48;ch[++now_r]>=48;)
  28. x=(x<<1)+(x<<3)+ch[now_r]-48;
  29. }
  30. int q2[max_n],tail2;
  31. void write(int x)
  32. {
  33. for (;x;x/=10) q2[++tail2]=x%10;
  34. for (;tail2;--tail2) ch[++now_w]=q2[tail2]+48;
  35. ch[++now_w]='\n';
  36. }
  37. struct edge{
  38. int to,next;
  39. }l[max_n<<1];
  40. struct qedge{
  41. int id,to,next;
  42. }ql[max_n<<1];
  43. struct node {
  44. int f;
  45. }T[max_n];
  46. void dfs_lca(int &x)
  47. {
  48. f[x]=x;b[x]=1;
  49. int y,i;
  50. for(i=t[x];i;i=l[i].next)
  51. if((y=l[i].to)!=T[x].f)
  52. {
  53. T[y].f=x;
  54. dfs_lca(y);
  55. f[y]=x;
  56. }
  57. for(i=qt[x];i;i=ql[i].next)
  58. if(b[y=ql[i].to])
  59. {
  60. while (y!=f[y])
  61. {
  62. q2[++tail2]=y;y=f[y];
  63. }
  64. lca[ql[i].id]=y;
  65. while (tail2)
  66. {
  67. f[q2[tail2]]=y;
  68. --tail2;
  69. }
  70. }
  71. }
  72. int main()
  73. {
  74. ch[fread(ch,1,top,stdin)]=0;
  75. int n,m,root,x,y;
  76. read(n);read(m);read(root);
  77. for (i=1;i<n;++i)
  78. {
  79. read(x);read(y);
  80. add_edge(x,y);add_edge(y,x);
  81. }
  82. for (i=1;i<=m;++i)
  83. {
  84. read(x);read(y);
  85. add_qedge(x,y);add_qedge(y,x);
  86. }
  87. dfs_lca(root);
  88. for (i=1;i<=m;++i) write(lca[i]);
  89. fwrite(ch,1,now_w,stdout);
  90. }

复杂度

强连通分量(缩点)

Kosaraju算法

【简介】

在计算机科学中,Kosaraju的算法(也称为Kosaraju-Sharir算法)是线性时间的算法来找到一个有向图的强连通分量。
Aho, HopcroftUllman相信这个算法是由S.RaoKosaraju在1978在一个未发表的论文上提出的。
相同的算法还从MichaSharir1981年自己出版的书上被单独的发现,这个算法利用了一个事实,即转置图(同图中的每边的方向相反)具有和原图完全一样的强连通分量。

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3387
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. #define N 100001
  6. #define M 1000001
  7. int x[M],y[M],head[M],nxt[M],v[M],w[M],cnt;
  8. int rhead[M],rnxt[M],rv[M],rcnt;
  9. int a[N],c[N],dp[N],mark[N],stack[N],n,m,t,ans;
  10. bool vis[N];
  11. void addline(int x,int y)
  12. {
  13. v[cnt]=y,nxt[cnt]=head[x],head[x]=cnt++;
  14. }
  15. void raddline(int x,int y)
  16. {
  17. rv[rcnt]=y,rnxt[rcnt]=rhead[x],rhead[x]=rcnt++;
  18. }
  19. void dfs1(int x)
  20. {
  21. vis[x]=true;
  22. for(int i=head[x];i!=-1;i=nxt[i])
  23. if(!vis[v[i]]) dfs1(v[i]);
  24. stack[++t]=x;
  25. return;
  26. }
  27. void dfs2(int x)
  28. {
  29. mark[x]=t,c[t]+=a[x];
  30. for(int i=rhead[x];i!=-1;i=rnxt[i])
  31. if(!mark[rv[i]]) dfs2(rv[i]);
  32. return;
  33. }
  34. void dfs3(int x)
  35. {
  36. if(dp[x]) return;
  37. for(int i=head[x];i!=-1;i=nxt[i])
  38. {
  39. if(!dp[v[i]]) dfs3(v[i]);
  40. dp[x]=std::max(dp[x],dp[v[i]]);
  41. }
  42. dp[x]+=c[x];
  43. return;
  44. }
  45. int main()
  46. {
  47. memset(head,-1,sizeof(head));
  48. memset(rhead,-1,sizeof(rhead));
  49. scanf("%d%d",&n,&m);
  50. for(int i=1;i<=n;i++)
  51. scanf("%d",&a[i]);
  52. for(int i=1;i<=m;i++)
  53. {
  54. scanf("%d%d",&x[i],&y[i]);
  55. addline(x[i],y[i]);
  56. raddline(y[i],x[i]);
  57. }
  58. for(int i=1;i<=n;i++)
  59. if(!vis[i]) dfs1(i);
  60. t=0,cnt=0;
  61. for(int i=n;i>=1;i--)
  62. if(!mark[stack[i]]) t++,dfs2(stack[i]);
  63. memset(head,-1,sizeof(head));
  64. memset(nxt,-1,sizeof(nxt));
  65. memset(vis,0,sizeof(vis));
  66. for(int i=1;i<=m;i++)
  67. if(mark[x[i]]!=mark[y[i]])
  68. addline(mark[x[i]],mark[y[i]]);
  69. for(int i=1;i<=t;i++)
  70. {
  71. if(!dp[i]) dfs3(i);
  72. ans=std::max(ans,dp[i]);
  73. }
  74. printf("%d\n",ans);
  75. return 0;
  76. }

复杂度

Tarjan算法

【简介】

Tarjan算法是用来求有向图的强连通分量的。求有向图的强连通分量的Tarjan算法是以其发明者Robert·Tarjan命名的。Robert·Tarjan还发明了求双连通分量的Tarjan算法。

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3387
  2. #include<cstdio>
  3. #include<stack>
  4. using namespace std;
  5. const int N=10001;
  6. bool used[N];
  7. int dfn[N],low[N],p1[N],p2[N],v[N],a[N],f[N],fa[N],num,ord,ans;
  8. struct edge1{
  9. int u,nxt;
  10. }e1[N*10];
  11. struct edge2{
  12. int u,nxt;
  13. }e2[N*10];
  14. stack<int>q;
  15. void add1(int y,int x)
  16. {
  17. e1[++num]=(edge1){y,p1[x]};
  18. p1[x]=num;
  19. }
  20. void add2(int x,int y)
  21. {
  22. e2[++num]=(edge2){y,p2[x]};
  23. p2[x]=num;
  24. }
  25. void dfs1(int s)
  26. {
  27. low[s]=dfn[s]=++ord;
  28. q.push(s),used[s]=1;
  29. for(int i=p1[s];i;i=e1[i].nxt)
  30. {
  31. int u=e1[i].u;
  32. if(!dfn[u])dfs1(u),low[s]=min(low[u],low[s]);
  33. else if(used[u])low[s]=min(low[s],dfn[u]);
  34. }
  35. if(dfn[s]==low[s])
  36. {
  37. while(q.top()!=s)
  38. {
  39. v[s]+=v[q.top()];
  40. fa[q.top()]=s;
  41. used[q.top()]=0,q.pop();
  42. }
  43. q.pop(),used[s]=0,fa[s]=s;
  44. }
  45. }
  46. void dfs2(int s)
  47. {
  48. f[s]+=v[s];
  49. ans=max(ans,f[s]);
  50. for(int i=p2[s];i;i=e2[i].nxt)
  51. {
  52. int u=e2[i].u;
  53. a[u]--,f[u]=max(f[u],f[s]);
  54. if(!a[u]) dfs2(u);
  55. }
  56. }
  57. int main()
  58. {
  59. int n,m,x,y;
  60. scanf("%d%d",&n,&m);
  61. for(int i=1;i<=n;++i) scanf("%d",&v[i]);
  62. while(m--) scanf("%d%d",&x,&y),add1(x,y);
  63. num=0;
  64. for(int i=1;i<=n;++i)
  65. if(!dfn[i]) dfs1(i);
  66. for(int i=1;i<=n;++i)
  67. for(int j=p1[i];j;j=e1[j].nxt)
  68. if(fa[i]!=fa[e1[j].u])
  69. {
  70. add2(fa[i],fa[e1[j].u]);
  71. a[fa[e1[j].u]]++;
  72. }
  73. for(int i=1;i<=n;++i)
  74. if(!a[i]&&!f[i]&&fa[i]==i) dfs2(i);
  75. printf("%d",ans);
  76. return 0;
  77. }

复杂度

二分图算法

匈牙利二分图最大匹配算法

【代码实现】(邻接矩阵)
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3386
  5. #include<cstdio>
  6. #include<cstring>
  7. int n,m,e,gril[1001],ans;
  8. bool f[1001][1001],vis[1001];
  9. bool dfs(int now)
  10. {
  11. for(int i=1;i<=m;i++)
  12. {
  13. if(f[now][i]&&(!vis[i]))
  14. {
  15. vis[i]=1;
  16. if(!gril[i]||dfs(gril[i])) {gril[i]=now;return true;}
  17. }
  18. }
  19. return false;
  20. }
  21. int main()
  22. {
  23. scanf("%d%d%d",&n,&m,&e);
  24. int x,y;
  25. for(int i=1;i<=e;i++)
  26. {
  27. scanf("%d%d",&x,&y);
  28. f[x][y]=true;
  29. }
  30. for(int i=1;i<=n;i++)
  31. {
  32. memset(vis,false,sizeof(vis));
  33. if(dfs(i)) ans++;
  34. }
  35. printf("%d\n",ans);
  36. return 0;
  37. }

复杂度

【代码实现】(邻接表)
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3386
  5. #include<cstdio>
  6. #include<cstring>
  7. struct Edge{
  8. int nxt,to;
  9. }e[1000001];
  10. int n,m,tot,q,gril[1001],head[1001],ans;
  11. bool vis[1001];
  12. void AddEdge(int u,int v)
  13. {
  14. e[++tot].to=v;e[tot].nxt=head[u];head[u]=tot;
  15. }
  16. bool dfs(int now)
  17. {
  18. for(int i=head[now];i;i=e[i].nxt)
  19. {
  20. if(!vis[e[i].to])
  21. {
  22. vis[e[i].to]=1;
  23. if(!gril[e[i].to]||dfs(gril[e[i].to])) {gril[e[i].to]=now;return true;}
  24. }
  25. }
  26. return false;
  27. }
  28. int main()
  29. {
  30. scanf("%d%d%d",&n,&m,&q);
  31. int x,y;
  32. for(int i=1;i<=q;i++)
  33. {
  34. scanf("%d%d",&x,&y);
  35. if(y>m) continue;
  36. AddEdge(x,y);
  37. }
  38. for(int i=1;i<=n;i++)
  39. {
  40. memset(vis,false,sizeof(vis));
  41. if(dfs(i)) ans++;
  42. }
  43. printf("%d\n",ans);
  44. return 0;
  45. }

复杂度

Dinic实现二分图最大匹配

【代码实现】
  1. /*
  2. Coded by Avalon
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3386
  5. #include<cstdio>
  6. #include<cstring>
  7. #include<iostream>
  8. #include<algorithm>
  9. #include<queue>
  10. using namespace std;
  11. const int maxn=1e6,inf=1e9;
  12. inline int read()
  13. {
  14. register int X=0;register char ch=0;
  15. while(ch<'0'||ch>'9')ch=getchar();
  16. while(ch>='0'&&ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
  17. return X;
  18. }
  19. inline void write(int x)
  20. {
  21. if(x>9) write(x/10);
  22. putchar(x%10+'0');
  23. }
  24. struct edge{
  25. int next,to,c,f;
  26. }e[maxn];
  27. int head[maxn],cur[maxn],lev[maxn],s,t,n,m,tot,ed;
  28. void add(int u,int v,int c)
  29. {
  30. e[tot].next=head[u],e[tot].c=c,e[tot].f=0,e[tot].to=v,head[u]=tot++;
  31. e[tot].next=head[v],e[tot].c=0,e[tot].f=0,e[tot].to=u,head[v]=tot++;
  32. }
  33. bool bfs()
  34. {
  35. memset(lev,0,sizeof(lev));
  36. queue<int> q;
  37. q.push(s);
  38. lev[s]=1;
  39. while(!q.empty())
  40. {
  41. int u=q.front();
  42. q.pop();
  43. for(int i=head[u];i!=-1;i=e[i].next)
  44. {
  45. int v=e[i].to;
  46. if(!lev[v]&&e[i].c>e[i].f)
  47. {
  48. q.push(v);
  49. lev[v]=lev[u]+1;
  50. }
  51. }
  52. }
  53. return lev[t]!=0;
  54. }
  55. int dfs(int u,int cpf)
  56. {
  57. if(u==t) return cpf;
  58. int adf=0;
  59. for(int i=(cur[u]!=0?cur[u]:head[u]);i!=-1&&adf<cpf;i=e[i].next)
  60. {
  61. int v=e[i].to;
  62. if(lev[v]==lev[u]+1&&e[i].c>e[i].f)
  63. {
  64. int tmp=dfs(v,min(e[i].c-e[i].f,cpf-adf));
  65. e[i].f+=tmp,e[i^1].f-=tmp;
  66. adf+=tmp;
  67. cur[u]=i;
  68. }
  69. }
  70. return adf;
  71. }
  72. int dinic()
  73. {
  74. int maxflow=0;
  75. while(bfs())
  76. {
  77. memset(cur,0,sizeof(cur));
  78. maxflow+=dfs(s,inf);
  79. }
  80. return maxflow;
  81. }
  82. int main()
  83. {
  84. n=read(),m=read(),ed=read(),s=n+m+3,t=n+m+4;
  85. memset(head,-1,sizeof(head));
  86. for(int i=1;i<=ed;i++)
  87. {
  88. int u=read(),v=read();
  89. add(u,v+n,1);
  90. }
  91. for(int i=1;i<=n;i++) add(s,i,1);
  92. for(int i=n+1;i<=m+n;i++) add(i,t,1);
  93. write(dinic()),putchar('\n');
  94. }

复杂度:由于流量为1,Dinic算法在二分图上的复杂度为

*KM最大权匹配算法

【代码实现】
  1. //problemID:http://www.uoj.ac/problem/80
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. typedef int ll;
  5. const int maxn=407,inf=1e9;
  6. int nl,nr,m;
  7. struct KuhnMunkres
  8. {
  9. int n;//左边0~n-1个点,右边0~n-1个点
  10. int a[maxn+5][maxn+5];
  11. int lx[maxn+5],ly[maxn+5],sla[maxn+5];//hl是左边顶标 hr是右边顶标
  12. int fl[maxn+5],fr[maxn+5];//fl[i]表示左边第i个点匹配右边哪个点 fr[i]表示右边第i个点匹配哪个点
  13. int vx[maxn+5],vy[maxn+5],pre[maxn+5];
  14. int q[maxn+5],tp;
  15. void match(int x)
  16. {
  17. while(x)
  18. {
  19. fr[x]=pre[x];
  20. int y=fl[fr[x]];
  21. fl[fr[x]]=x;
  22. x=y;
  23. }
  24. }
  25. void find(int x)
  26. {
  27. fill(vx,vx+n+1,0);
  28. fill(vy,vy+n+1,0);
  29. fill(sla,sla+n+1,inf);
  30. q[tp=1]=x;vx[x]=1;
  31. while(1)
  32. {
  33. for(int i=1;i<=tp;i++)
  34. {
  35. int x=q[i];
  36. for(int y=1;y<=n;y++)
  37. {
  38. int t=lx[x]+ly[y]-a[x][y];
  39. if(vy[y]||t>sla[y])continue;
  40. pre[y]=x;
  41. if(t==0)
  42. {
  43. if(!fr[y]){match(y);return;}
  44. q[++tp]=fr[y];vy[y]=1;vx[fr[y]]=1;
  45. }
  46. else sla[y]=t;
  47. }
  48. }
  49. int d=inf;tp=0;
  50. for(int i=1;i<=n;i++)if(!vy[i]&&d>sla[i])d=sla[i],x=i;
  51. for(int i=1;i<=n;i++)
  52. {
  53. if(vx[i])lx[i]-=d;
  54. if(vy[i])ly[i]+=d;
  55. else sla[i]-=d;
  56. }
  57. if(!fr[x]){match(x);return;}
  58. q[++tp]=fr[x];vy[x]=vx[fr[x]]=1;
  59. }
  60. }
  61. void solve()
  62. {
  63. memset(lx,0,sizeof(lx));
  64. memset(ly,0,sizeof(ly));
  65. memset(fl,0,sizeof(fl));
  66. memset(fr,0,sizeof(fr));
  67. for(int i=1;i<=n;++i) lx[i]=*max_element(a[i]+1,a[i]+n+1);
  68. for(int i=1;i<=n;++i) find(i);
  69. }
  70. }km;
  71. int main()
  72. {
  73. scanf("%d%d%d",&nl,&nr,&m);
  74. while(m--)
  75. {
  76. int u,v;
  77. scanf("%d%d",&u,&v);
  78. scanf("%d",&km.a[u][v]);
  79. }
  80. km.n=max(nl,nr);
  81. km.solve();
  82. long long ans=0;
  83. for(int i=1;i<=nl;++i)ans+=km.a[i][km.fl[i]];
  84. printf("%lld\n",ans);
  85. for(int i=1;i<=nl;++i)
  86. if(km.a[i][km.fl[i]]==0) printf("0 ");
  87. else
  88. printf("%d ",km.fl[i]);
  89. return 0;
  90. }

复杂度

*网络流算法

Edmonds-Karp最大流算法

【简介】

EK算法基于一个基本的方法:Ford-Fulkerson方法 即增广路方法 简称FF方法。

增广路方法是很多网络流算法的基础一般都在残留网络中实现其思路是每次找出一条从源到汇的能够增加流的路径 调整流值和残留网络 不断调整直到没有增广路为止

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3376
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<queue>
  6. using namespace std;
  7. const int INF=0x7ffffff;
  8. queue <int> q;
  9. int n,m,x,y,s,t,g[201][201],pre[201],flow[201],maxflow;
  10. //g邻接矩阵存图,pre增广路径中每个点的前驱,flow源点到这个点的流量
  11. inline int bfs(int s,int t)
  12. {
  13. while (!q.empty()) q.pop();
  14. for (int i=1; i<=n; i++) pre[i]=-1;
  15. pre[s]=0;
  16. q.push(s);
  17. flow[s]=INF;
  18. while (!q.empty())
  19. {
  20. int x=q.front();
  21. q.pop();
  22. if (x==t) break;
  23. for (int i=1; i<=n; i++)
  24. //EK一次只找一个增广路
  25. if (g[x][i]>0 && pre[i]==-1)
  26. {
  27. pre[i]=x;
  28. flow[i]=min(flow[x],g[x][i]);
  29. q.push(i);
  30. }
  31. }
  32. if (pre[t]==-1) return -1;
  33. else return flow[t];
  34. }
  35. //increase为增广的流量
  36. void EK(int s,int t)
  37. {
  38. int increase=0;
  39. while ((increase=bfs(s,t))!=-1)
  40. {//迭代
  41. int k=t;
  42. while (k!=s)
  43. {
  44. int last=pre[k];//从后往前找路径
  45. g[last][k]-=increase;
  46. g[k][last]+=increase;
  47. k=last;
  48. }
  49. maxflow+=increase;
  50. }
  51. }
  52. int main()
  53. {
  54. scanf("%d%d",&m,&n);
  55. for (int i=1; i<=m; i++)
  56. {
  57. int z;
  58. scanf("%d%d%d",&x,&y,&z);
  59. g[x][y]+=z;
  60. }
  61. EK(1,n);
  62. printf("%d",maxflow);
  63. return 0;
  64. }

复杂度

Dinic最大流算法

【简介】

dinic算法在EK算法的基础上增加了分层图的概念,根据从到各个点的最短距离的不同,把整个图分层。寻找的增广路要求满足所有的点分别属于不同的层,且若增广路为,点在分层图中的所属的层记为,那么应满足

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3376
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<queue>
  6. using namespace std;
  7. const int inf=1e9;
  8. int n,m,x,y,z,maxflow,deep[500];//deep深度
  9. struct Edge{
  10. int next,to,dis;
  11. }edge[500];
  12. int num_edge=-1,head[500],cur[500];//cur用于复制head
  13. queue <int> q;
  14. void add_edge(int from,int to,int dis,bool flag)
  15. {
  16. edge[++num_edge].next=head[from];
  17. edge[num_edge].to=to;
  18. if (flag) edge[num_edge].dis=dis;//反图的边权为 0
  19. head[from]=num_edge;
  20. }
  21. //bfs用来分层
  22. bool bfs(int s,int t)
  23. {
  24. memset(deep,0x7f,sizeof(deep));
  25. while (!q.empty()) q.pop();
  26. for (int i=1; i<=n; i++) cur[i]=head[i];
  27. deep[s]=0;
  28. q.push(s);
  29. while (!q.empty())
  30. {
  31. int now=q.front(); q.pop();
  32. for (int i=head[now]; i!=-1; i=edge[i].next)
  33. {
  34. if (deep[edge[i].to]>inf && edge[i].dis)//dis在此处用来做标记 是正图还是返图
  35. {
  36. deep[edge[i].to]=deep[now]+1;
  37. q.push(edge[i].to);
  38. }
  39. }
  40. }
  41. if (deep[t]<inf) return true;
  42. else return false;
  43. }
  44. //dfs找增加的流的量
  45. int dfs(int now,int t,int limit)//limit为源点到这个点的路径上的最小边权
  46. {
  47. if (!limit || now==t) return limit;
  48. int flow=0,f;
  49. for (int i=cur[now]; i!=-1; i=edge[i].next)
  50. {
  51. cur[now]=i;
  52. if (deep[edge[i].to]==deep[now]+1 && (f=dfs(edge[i].to,t,min(limit,edge[i].dis))))
  53. {
  54. flow+=f;
  55. limit-=f;
  56. edge[i].dis-=f;
  57. edge[i^1].dis+=f;
  58. if (!limit) break;
  59. }
  60. }
  61. return flow;
  62. }
  63. void Dinic(int s,int t)
  64. {
  65. while (bfs(s,t))
  66. maxflow+=dfs(s,t,inf);
  67. }
  68. int main()
  69. {
  70. // for (int i=0; i<=500; i++) edge[i].next=-1;
  71. memset(head,-1,sizeof(head));
  72. scanf("%d%d",&m,&n);
  73. for (int i=1; i<=m; i++)
  74. {
  75. scanf("%d%d%d",&x,&y,&z);
  76. add_edge(x,y,z,1); add_edge(y,x,z,0);
  77. }
  78. Dinic(1,n);
  79. printf("%d",maxflow);
  80. return 0;
  81. }

复杂度

*ISAP最大流算法

【简介】

ISAP(Improved Shortest Augmenting Path)也是基于分层思想的最大流算法。所不同的是,它省去了Dinic每次增广后需要重新构建分层图的麻烦,而是在每次增广完成后自动更新每个点的『标号』(也就是所在的层)

最短增广路算法是一种运用距离标号使寻找增广路的时间复杂度下降的算法。所谓的距离标号就是某个点到汇点的最少的弧的数量(即当边权为1时某个点的最短路径长度). 设点的标号为, 那么如果将满足, 且增广时只走允许弧, 那么就可以达到"怎么走都是最短路"的效果. 每个点的初始标号可以在一开始用一次从汇点沿所有反向的BFS求出.

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3376
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<iostream>
  6. #include<queue>
  7. using namespace std;
  8. const int inf=1e9;
  9. queue <int> q;
  10. int m,n,x,y,z,maxflow,head[5000],num_edge=-1;
  11. int cur[5000],deep[5000],last[5000],num[5000];
  12. //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
  13. struct Edge{
  14. int next,to,dis;
  15. }edge[500];
  16. void add_edge(int from,int to,int dis,bool flag)
  17. {
  18. edge[++num_edge].next=head[from];
  19. edge[num_edge].to=to;
  20. edge[num_edge].dis=dis;
  21. head[from]=num_edge;
  22. }
  23. //bfs仅用于更新deep
  24. void bfs(int t)
  25. {
  26. while (!q.empty()) q.pop();
  27. for (int i=0; i<=m; i++) cur[i]=head[i];
  28. for (int i=1; i<=n; i++) deep[i]=n;
  29. deep[t]=0;
  30. q.push(t);
  31. while (!q.empty())
  32. {
  33. int now=q.front(); q.pop();
  34. for (int i=head[now]; i!=-1; i=edge[i].next)
  35. {
  36. if (deep[edge[i].to]==n && edge[i^1].dis)//i^1是为了找反边
  37. {
  38. deep[edge[i].to]=deep[now]+1;
  39. q.push(edge[i].to);
  40. }
  41. }
  42. }
  43. }
  44. int add_flow(int s,int t)
  45. {
  46. int ans=inf,now=t;
  47. while (now!=s)
  48. {
  49. ans=min(ans,edge[last[now]].dis);
  50. now=edge[last[now]^1].to;
  51. }
  52. now=t;
  53. while (now!=s)
  54. {
  55. edge[last[now]].dis-=ans;
  56. edge[last[now]^1].dis+=ans;
  57. now=edge[last[now]^1].to;
  58. }
  59. return ans;
  60. }
  61. void isap(int s,int t)
  62. {
  63. int now=s;
  64. bfs(t);//搜出一条增广路
  65. for (int i=1; i<=n; i++) num[deep[i]]++;
  66. while (deep[s]<n)
  67. {
  68. if (now==t)
  69. {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
  70. maxflow+=add_flow(s,t);
  71. now=s;//回到源点
  72. }
  73. bool has_find=0;
  74. for (int i=cur[now]; i!=-1; i=edge[i].next)
  75. {
  76. if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
  77. {
  78. has_find=true;
  79. cur[now]=i;//当前弧优化
  80. now=edge[i].to;
  81. last[edge[i].to]=i;
  82. break;
  83. }
  84. }
  85. if (!has_find)//没有找到出边,重新编号
  86. {
  87. int minn=n-1;
  88. for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
  89. if (edge[i].dis)
  90. minn=min(minn,deep[edge[i].to]);
  91. if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
  92. num[deep[now]=minn+1]++;
  93. cur[now]=head[now];
  94. if (now!=s)
  95. now=edge[last[now]^1].to;
  96. }
  97. }
  98. }
  99. int main()
  100. {
  101. memset(head,-1,sizeof(head));
  102. scanf("%d%d",&m,&n);
  103. for (int i=1; i<=m; i++)
  104. {
  105. scanf("%d%d%d",&x,&y,&z);
  106. add_edge(x,y,z,1); add_edge(y,x,z,0);
  107. }
  108. isap(1,n);
  109. printf("%d",maxflow);
  110. return 0;
  111. }

复杂度,但在非二分图上表现得比Dinic要好。

*HLPP最高标号预流推进算法

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P4722
  2. #include<iostream>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<queue>
  7. using namespace std;
  8. const int MAXN=2*1e3+10;
  9. const int INF=1e8+10;
  10. inline char nc()
  11. {
  12. static char buf[MAXN],*p1=buf,*p2=buf;
  13. return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
  14. }
  15. inline int read()
  16. {
  17. char c=nc();int x=0,f=1;
  18. while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();}
  19. while(c>='0'&&c<='9'){x=x*10+c-'0';c=nc();}
  20. return x*f;
  21. }
  22. int N,M,S,T;
  23. int H[MAXN];//每个节点的高度
  24. int F[MAXN];//每个节点可以流出的流量
  25. int gap[MAXN];//每个高度的数量
  26. struct node
  27. {
  28. int u,v,flow,nxt;
  29. }edge[MAXN];
  30. int head[MAXN];
  31. int num=0;//注意这里num必须从0开始
  32. inline void add_edge(int x,int y,int z)
  33. {
  34. edge[num].u=x;
  35. edge[num].v=y;
  36. edge[num].flow=z;
  37. edge[num].nxt=head[x];
  38. head[x]=num++;
  39. }
  40. inline void AddEdge(int x,int y,int z)
  41. {
  42. add_edge(x,y,z);
  43. add_edge(y,x,0);//注意这里别忘了加反向边
  44. }
  45. struct comp
  46. {
  47. int pos,h;
  48. comp(int pos=0,int h=0):pos(pos),h(h) {}
  49. inline bool operator < (const comp &a) const {return h<a.h;}
  50. };
  51. priority_queue<comp>q;
  52. bool Work(int u,int v,int id)
  53. {
  54. int val=min(F[u],edge[id].flow);
  55. edge[id].flow-=val;edge[id^1].flow+=val;
  56. F[u]-=val;F[v]+=val;
  57. return val;
  58. }
  59. inline int HLPP()
  60. {
  61. H[S]=N;F[S]=INF;q.push(comp(S,H[S]));
  62. while(q.size()!=0)
  63. {
  64. int p=q.top().pos;q.pop();
  65. if(!F[p]) continue;
  66. for(int i=head[p];i!=-1;i=edge[i].nxt)
  67. if( (p==S||H[edge[i].v]+1==H[p]) && Work(p,edge[i].v,i) && edge[i].v!=S && edge[i].v!=T)
  68. q.push( comp(edge[i].v,H[edge[i].v]) );
  69. if(p!=S && p!=T && F[p])
  70. {
  71. if( (--gap[ H[p] ])==0 )//该高度不存在
  72. {
  73. for(int i=1;i<=N;i++)
  74. if( H[p]<H[i]&&H[i]<=N && p!=S && p!=T )
  75. H[i]=N+1;//设置为不可访问
  76. }
  77. ++gap[ ++H[p] ];//高度+1
  78. q.push( comp(p,H[p]) );
  79. }
  80. }
  81. return F[T];
  82. }
  83. int main()
  84. {
  85. memset(head,-1,sizeof(head));
  86. N=read(),M=read(),S=read(),T=read();
  87. for(int i=1;i<=M;i++)
  88. {
  89. int x=read(),y=read(),z=read();
  90. AddEdge(x,y,z);
  91. }
  92. printf("%d", HLPP() );
  93. return 0;
  94. }

复杂度

*zkw最小费用最大流

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3381
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <map>
  5. #include <math.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <algorithm>
  9. #define MAXN 50050
  10. #define MAXN_ 5050
  11. #define INF 0x3f3f3f3f
  12. using namespace std;
  13. struct edge{ int to,cap,cost,rev;};
  14. int n,m,flow,s,t,cap,res,cost,from,to;
  15. std::vector<edge> G[MAXN_];
  16. int dist[MAXN_],prevv[MAXN_],preve[MAXN_]; // 最短路的前驱节点 和 对应的边
  17. inline void add()
  18. {
  19. //也许你看到这里会看不懂,为什么要存一个 size 进来, 那么别急,我们下边的存反边 会用到的!
  20. G[from].push_back((edge){to,cap,cost,(int)G[to].size()});
  21. G[to].push_back((edge){from,0,-cost,(int)G[from].size()-1});
  22. }
  23. inline int read()
  24. {
  25. int x=0;
  26. char c=getchar();
  27. bool flag=0;
  28. while(c<'0'||c>'9'){if(c=='-')flag=1; c=getchar();}
  29. while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
  30. return flag?-x:x;
  31. }
  32. inline void min_cost_flow(int s,int t,int f)
  33. {
  34. while(f > 0)
  35. {
  36. memset(dist,INF,sizeof dist);
  37. dist[s] = 0;
  38. bool update = true;
  39. while(update)
  40. {
  41. update = false;
  42. for(int i=1;i<=n;i++)
  43. {
  44. if(dist[i] == INF) continue;
  45. for(int j=0;j<(int)G[i].size(); ++j)
  46. {
  47. edge &e = G[i][j];
  48. if(e.cap > 0 && dist[e.to] > dist[i] + e.cost)
  49. {
  50. dist[e.to] = dist[i] + e.cost;
  51. prevv[e.to] = i;
  52. preve[e.to] = j;
  53. update = true;
  54. }
  55. }
  56. }
  57. }
  58. // 此时的 INF 表示再也没有 增广路径,于是就是最后的答案了!
  59. if(dist[t] == INF) break;
  60. int d = f;
  61. for(int v = t; v != s; v = prevv[v])
  62. d = min(d,G[prevv[v]][preve[v]].cap);
  63. // d 就是 增广路的 流量!
  64. f -= cap;
  65. flow += d;
  66. res += d * dist[t];
  67. for(int v=t;v!=s;v=prevv[v])
  68. {
  69. edge &e = G[prevv[v]][preve[v]];
  70. e.cap -= d;
  71. G[v][e.rev].cap += d; // 给 反边加上适当的边权!
  72. }
  73. }
  74. }
  75. int main(int argc,char const* argv[])
  76. {
  77. n = read(); m = read(); s = read(); t = read();
  78. for(int i=1;i<=m;++i)
  79. {
  80. scanf("%d%d%d%d",&from,&to,&cap,&cost);
  81. add();
  82. }
  83. min_cost_flow(s,t,INF);
  84. printf("%d %d\n",flow,res);
  85. return 0;
  86. }

*Primal-Dual原始对偶算法

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P3381
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <queue>
  5. #include <math.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <algorithm>
  9. #define MAXN_ 5050
  10. #define INF 0x3f3f3f3f
  11. #define P pair<int,int>
  12. using namespace std;
  13. struct edge{ int to,cap,cost,rev;};
  14. int n,m,flow,s,t,cap,res,cost,from,to,h[MAXN_];
  15. std::vector<edge> G[MAXN_];
  16. int dist[MAXN_],prevv[MAXN_],preve[MAXN_]; // 前驱节点和对应边
  17. inline void add()
  18. {
  19. G[from].push_back((edge){to,cap,cost,(int)G[to].size()});
  20. G[to].push_back((edge){from,0,-cost,(int)G[from].size()-1});
  21. } // 在vector 之中找到边的位置所在!
  22. inline int read()
  23. {
  24. int x=0;
  25. char c=getchar();
  26. bool flag=0;
  27. while(c<'0'||c>'9'){if(c=='-')flag=1; c=getchar();}
  28. while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
  29. return flag?-x:x;
  30. }
  31. inline void min_cost_flow(int s,int t,int f)
  32. {
  33. fill(h+1,h+1+n,0);
  34. while(f > 0)
  35. {
  36. priority_queue<P,vector<P>, greater<P> > D;
  37. memset(dist,INF,sizeof dist);
  38. dist[s] = 0; D.push(P(0,s));
  39. while(!D.empty())
  40. {
  41. P now = D.top(); D.pop();
  42. if(dist[now.second] < now.first) continue;
  43. int v = now.second;
  44. for(int i=0;i<(int)G[v].size();++i)
  45. {
  46. edge &e = G[v][i];
  47. if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
  48. {
  49. dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
  50. prevv[e.to] = v;
  51. preve[e.to] = i;
  52. D.push(P(dist[e.to],e.to));
  53. }
  54. }
  55. }
  56. // 无法增广 , 就是找到了答案了!
  57. if(dist[t] == INF) break;
  58. for(int i=1;i<=n;++i) h[i] += dist[i];
  59. int d = f;
  60. for(int v = t; v != s; v = prevv[v])
  61. d = min(d,G[prevv[v]][preve[v]].cap);
  62. f -= d; flow += d;
  63. res += d * h[t];
  64. for(int v=t;v!=s;v=prevv[v])
  65. {
  66. edge &e = G[prevv[v]][preve[v]];
  67. e.cap -= d;
  68. G[v][e.rev].cap += d;
  69. }
  70. }
  71. }
  72. int main(int argc,char const* argv[])
  73. {
  74. n = read(); m = read(); s = read(); t = read();
  75. for(int i=1;i<=m;++i)
  76. {
  77. from = read(); to = read(); cap = read(); cost = read();
  78. add();
  79. }
  80. min_cost_flow(s,t,INF);
  81. printf("%d %d\n",flow,res);
  82. return 0;
  83. }

*上下界最大流

【代码实现】
  1. #include<iostream>
  2. #include<string>
  3. #include<algorithm>
  4. #include<cstdlib>
  5. #include<cstdio>
  6. #include<set>
  7. #include<map>
  8. #include<vector>
  9. #include<cstring>
  10. #include<stack>
  11. #include<cmath>
  12. #include<queue>
  13. using namespace std;
  14. #define CL(x,v); memset(x,v,sizeof(x));
  15. #define INF 0x3f3f3f3f
  16. #define LL long long
  17. #define REP(i,r,n) for(int i=r;i<=n;i++)
  18. #define RREP(i,n,r) for(int i=n;i>=r;i--)
  19. const int MAXN=1500;
  20. struct Edge{
  21. int from,to,cap,flow;
  22. };
  23. bool cmp(const Edge& a,const Edge& b){
  24. return a.from < b.from || (a.from == b.from && a.to < b.to);
  25. }
  26. struct Dinic{
  27. int n,m,s,t;
  28. vector<Edge> edges;
  29. vector<int> G[MAXN];
  30. bool vis[MAXN];
  31. int d[MAXN];
  32. int cur[MAXN];
  33. void init(int n){
  34. this->n=n;
  35. for(int i=0;i<=n;i++)G[i].clear();
  36. edges.clear();
  37. }
  38. void AddEdge(int from,int to,int cap){
  39. edges.push_back((Edge){from,to,cap,0});
  40. edges.push_back((Edge){to,from,0,0});//当是无向图时,反向边容量也是cap,有向边时,反向边容量是0
  41. m=edges.size();
  42. G[from].push_back(m-2);
  43. G[to].push_back(m-1);
  44. }
  45. bool BFS(){
  46. CL(vis,0);
  47. queue<int> Q;
  48. Q.push(s);
  49. d[s]=0;
  50. vis[s]=1;
  51. while(!Q.empty()){
  52. int x=Q.front();
  53. Q.pop();
  54. for(int i=0;i<G[x].size();i++){
  55. Edge& e=edges[G[x][i]];
  56. if(!vis[e.to]&&e.cap>e.flow){
  57. vis[e.to]=1;
  58. d[e.to]=d[x]+1;
  59. Q.push(e.to);
  60. }
  61. }
  62. }
  63. return vis[t];
  64. }
  65. int DFS(int x,int a){
  66. if(x==t||a==0)return a;
  67. int flow=0,f;
  68. for(int& i=cur[x];i<G[x].size();i++){
  69. Edge& e=edges[G[x][i]];
  70. if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
  71. e.flow+=f;
  72. edges[G[x][i]^1].flow-=f;
  73. flow+=f;
  74. a-=f;
  75. if(a==0)break;
  76. }
  77. }
  78. return flow;
  79. }
  80. //当所求流量大于need时就退出,降低时间
  81. int Maxflow(int s,int t,int need){
  82. this->s=s;this->t=t;
  83. int flow=0;
  84. while(BFS()){
  85. CL(cur,0);
  86. flow+=DFS(s,INF);
  87. if(flow>need)return flow;
  88. }
  89. return flow;
  90. }
  91. //最小割割边
  92. vector<int> Mincut(){
  93. BFS();
  94. vector<int> ans;
  95. for(int i=0;i<edges.size();i++){
  96. Edge& e=edges[i];
  97. if(vis[e.from]&&!vis[e.to]&&e.cap>0)ans.push_back(i);
  98. }
  99. return ans;
  100. }
  101. void Reduce(){
  102. for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
  103. }
  104. void ClearFlow(){
  105. for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
  106. }
  107. };
  108. int n,m;
  109. Dinic solver;
  110. int du[MAXN];
  111. int dn[MAXN][MAXN];
  112. int id[MAXN][MAXN];
  113. int main()
  114. {
  115. while(~scanf("%d%d",&n,&m))
  116. {
  117. int s=0,t=n+m+1;
  118. int ss=n+m+2,tt=n+m+3;
  119. int a,b,c;
  120. CL(du,0);
  121. CL(dn,0);
  122. CL(id,0);
  123. solver.init(n+m+5);
  124. REP(i,1,m)
  125. {
  126. scanf("%d",&a);
  127. solver.AddEdge(n+i,t,INF-a);
  128. du[n+i]-=a;
  129. du[t]+=a;
  130. }
  131. int C,D;
  132. REP(i,1,n)
  133. {
  134. scanf("%d%d",&C,&D);
  135. solver.AddEdge(s,i,D);
  136. REP(j,1,C)
  137. {
  138. scanf("%d%d%d",&a,&b,&c);
  139. solver.AddEdge(i,a+n+1,c-b);
  140. du[i]-=b;
  141. du[a+n+1]+=b;
  142. dn[i][a]=b;
  143. id[i][a]=solver.edges.size()-2;
  144. }
  145. }
  146. solver.AddEdge(t,s,INF);
  147. int sum=0;
  148. REP(i,1,t)
  149. {
  150. if(du[i]<0)
  151. {
  152. solver.AddEdge(i,tt,-du[i]);
  153. }
  154. else if(du[i]>0)
  155. {
  156. solver.AddEdge(ss,i,du[i]);
  157. sum+=du[i];
  158. }
  159. }
  160. int maxflow=solver.Maxflow(ss,tt,INF);
  161. if(maxflow==sum)
  162. {
  163. int ans=solver.Maxflow(s,t,INF);
  164. printf("%d\n",ans);
  165. for(int i=1;i<=n;i++)
  166. {
  167. for(int j=0;j<m;j++)
  168. if(id[i][j])
  169. printf("%d\n",solver.edges[id[i][j]].flow+dn[i][j]);
  170. }
  171. }
  172. else printf("-1\n");
  173. printf("\n");
  174. }
  175. return 0;
  176. }

动态规划

简单线性动态规划

LIS(最长上升子序列)

【简介】

最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列。

【代码实现】
朴素dp
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/1576/
  5. #include<cstdio>
  6. #include<iostream>
  7. using namespace std;
  8. int n,a[5001],dp[5001],ans;
  9. int main()
  10. {
  11. scanf("%d",&n);dp[1]=1;
  12. for(int i=1;i<=n;i++) scanf("%d",&a[i]),dp[i]=1;
  13. for(int i=2;i<=n;i++)
  14. for(int j=1;j<i;j++)
  15. if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1),ans=max(ans,dp[i]);
  16. printf("%d",ans);
  17. return 0;
  18. }

复杂度

贪心实现
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3955/
  5. #include<cstdio>
  6. #include<algorithm>
  7. using namespace std;
  8. int n,f[1000001],len,x;
  9. int main()
  10. {
  11. scanf("%d%d",&n,&x);
  12. f[++len]=x;
  13. for(int i=2;i<=n;i++)
  14. {
  15. scanf("%d",&x);
  16. if(x>f[len]) f[++len]=x;
  17. else f[lower_bound(f+1,f+len+1,x)-f]=x;
  18. }
  19. printf("%d",len);
  20. return 0;
  21. }

复杂度

LCS(最长公共子序列)

【简介】

LCS是Longest Common Subsequence的缩写,即最长公共子序列。一个序列,如果是两个或多个已知序列的子序列,且是所有子序列中最长的,则为最长公共子序列。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1439
  5. #include<cstdio>
  6. #include<algorithm>
  7. using namespace std;
  8. int n,a[1001],b[1001],dp[1001][1001];
  9. int main()
  10. {
  11. scanf("%d",&n);
  12. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  13. for(int i=1;i<=n;i++) scanf("%d",&b[i]);
  14. for(int i=1;i<=n;i++)
  15. for(int j=1;j<=n;j++)
  16. if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
  17. else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
  18. printf("%d",dp[n][n]);
  19. return 0;
  20. }

复杂度

LCIS(最长公共上升子序列)

【简介】

给定两个整数序列,求它们的最长上升公共子序列。

【代码实现】
  1. //problemID:http://acm.hdu.edu.cn/showproblem.php?pid=1423
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #define max(a,b) ((a)>(b)?(a):(b))
  5. #define min(a,b) ((a)<(b)?(a):(b))
  6. int lengthOfLCIS(int *a,int n,int *b,int m)
  7. {
  8. int ans=0;
  9. int dp[2][505]= {0};
  10. for(int i=0;i<n;++i)
  11. {
  12. int max_dp=0;
  13. for(int j=0;j<m;++j)
  14. {
  15. dp[(i+1)%2][j+1]=dp[i%2][j+1];
  16. if(a[i]>b[j]&&max_dp<dp[(i+1)%2][j+1]) max_dp=dp[(i+1)%2][j+1];
  17. if(a[i]==b[j]) dp[(i+1)%2][j+1]=max_dp+1;
  18. ans=max(ans,dp[(i+1)%2][j+1]);
  19. }
  20. }
  21. return ans;
  22. }
  23. int main()
  24. {
  25. int n,m,T;
  26. int a[505],b[505];
  27. scanf("%d",&T);
  28. while(T--)
  29. {
  30. scanf("%d",&n);
  31. for(int i=0;i<n;++i) scanf("%d",&a[i]);
  32. scanf("%d",&m);
  33. for(int j=0;j<m;++j) scanf("%d",&b[j]);
  34. printf("%d\n",lengthOfLCIS(a,n,b,m));
  35. if(T) printf("\n");
  36. }
  37. return 0;
  38. }

复杂度

最大子段和问题

【简介】

给定个整数(可能为负数)组成的序列求该序列如的子段和的最大值。当所给的整数均为负数时定义子段和为,依此定义,所求的最优值为:

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1115
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. #define min(x,y) (x)<(y)?(x):(y)
  8. int n,maxn,ans=-2147483647,pre,x;
  9. int main()
  10. {
  11. scanf("%d",&n);
  12. maxn=0;ans=-2147483647;
  13. for(int i=1;i<=n;i++)
  14. {
  15. scanf("%d",&x);
  16. x+=pre;
  17. pre=x;
  18. ans=max(ans,x-maxn);
  19. maxn=min(maxn,x);
  20. }
  21. printf("%d",ans);
  22. return 0;
  23. }

复杂度

背包问题动态规划

背包问题

【简介】

背包是在件物品取出若干件放在空间为的背包里,每件物品的体积为,与之相对应的价值为背包是背包问题中最简单的问题。背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/5709/
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. int n,f[5001],w[1001],k[1001],V;
  8. int main()
  9. {
  10. scanf("%d%d",&V,&n);
  11. for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&k[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=V;j>=w[i];j--)
  14. f[j]=max(f[j],f[j-w[i]]+k[i]);
  15. printf("%d",f[V]);
  16. return 0;
  17. }

复杂度

完全背包问题

【简介】

种物品和一个容量为的背包,每种物品都有无限件可用。
种物品的体积是,价值是。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1616
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. int n,f[100001],w[10001],k[10001],V;
  8. int main()
  9. {
  10. scanf("%d%d",&V,&n);
  11. for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&k[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=w[i];j<=V;j++)
  14. f[j]=max(f[j],f[j-w[i]]+k[i]);
  15. printf("%d",f[V]);
  16. return 0;
  17. }

复杂度

多重背包问题

【简介】

种物品和一个容量为的背包。第种物品最多有件可用,每件耗费的空间是,价值是。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

【代码实现】
朴素多重背包
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/5429/
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. int n,f[100001],w[7001],v[7001],c[7001],V;
  8. int main()
  9. {
  10. scanf("%d%d",&n,&V);
  11. for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=1;j<=c[i];j++)
  14. for(int k=V;k>=w[i];k--)
  15. f[k]=max(f[k],f[k-w[i]]+v[i]);
  16. printf("%d",f[V]);
  17. return 0;
  18. }

复杂度

二进制优化
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/5429/
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. int f[7005],w,v,k,s,n;
  8. int main()
  9. {
  10. scanf("%d%d",&n,&s);
  11. while(n--)
  12. {
  13. scanf("%d%d%d",&w,&v,&k);
  14. for(int d=1;d<k;k-=d,d<<=1)
  15. for(long long i=s;i>=w*d;i--)
  16. f[i]=max(f[i],f[i-w*d]+v*d);
  17. for(long long i=s;i>=k*w;i--)
  18. f[i]=max(f[i],f[i-k*w]+v*k);
  19. }
  20. printf("%d",f[s]);
  21. return 0;
  22. }

复杂度

单调队列优化
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/5429/
  5. #include<cstdio>
  6. int n,m,v[7001],w[7001],c[7001],dp[7001],queue[7001],front,rear,ind[7001];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++) scanf("%d%d%d",&v[i],&w[i],&c[i]);
  11. for(int i=1;i<=n;i++)
  12. {
  13. for(int d=0;d<v[i];d++)
  14. {
  15. front=0,rear=-1;
  16. for(int j=d,r=0;j<=m;j+=v[i],r++)
  17. {
  18. int g=dp[j]-w[i]*r;
  19. if(front<=rear&&ind[front]<j-c[i]*v[i]) front++;
  20. while(front<=rear&&queue[rear]<=g) rear--;
  21. queue[++rear]=g;ind[rear]=j;
  22. dp[j]=queue[front]+w[i]*r;
  23. }
  24. }
  25. }
  26. printf("%d",dp[m]);
  27. return 0;
  28. }

复杂度

混合背包问题

【简介】

有的物品只可以取一次(背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3269/
  5. #include<cstdio>
  6. #define max(x,y) (x)>(y)?(x):(y)
  7. int n,V,f[200005],h,t;
  8. void pack1(int v,int w)//01背包
  9. {
  10. for(int i=V;i>=v;i--)
  11. f[i]=max(f[i],f[i-v]+w);
  12. }
  13. void pack2(int v,int w)//完全背包
  14. {
  15. for(int i=v;i<=V;i++)
  16. f[i]=max(f[i],f[i-v]+w);
  17. }
  18. void pack3(int v,int w,int num)//多重背包二进制优化(虽然没用还是写了)
  19. {
  20. for(int k=1;k<num;num-=k,k<<=1) pack1(v*k,w*k);
  21. pack1(v*num,w*num);
  22. }
  23. struct node{
  24. int f,id;
  25. }q[200005];
  26. void pack4(int v,int w,int num)//多重背包单调队列优化
  27. {
  28. for(int i=0;i<v;i++)
  29. {
  30. h=t=0;
  31. for(int j=i,id=0;j<=V;j+=v,id++)
  32. {
  33. while(h!=t&&q[t-1].f<=f[j]-id*w) t--;
  34. q[t++]=(node){f[j]-id*w,id};
  35. while(h!=t&&id-q[h].id>num) h++;
  36. f[j]=q[h].f+id*w;
  37. }
  38. }
  39. }
  40. int main()
  41. {
  42. scanf("%d%d",&n,&V);
  43. for(int i=1,v,w,num;i<=n;i++)
  44. {
  45. scanf("%d%d%d",&v,&w,&num);
  46. if(num==-1||V/v<=num) pack2(v,w);
  47. else if(num==1) pack1(v,w);
  48. else pack4(v,w,num);
  49. }
  50. printf("%d",f[V]);
  51. return 0;
  52. }

复杂度

二维费用的背包问题

【简介】

二维费用的背包问题是指:对于每件物品,具有两种不同的费用,选择这件物品必须同时付出这两种费用。对于每种费用都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。
设第件物品所需的两种费用分别为。两种费用可付出的最大值(也即两种背包容量)分别为。物品的价值为

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://acm.hdu.edu.cn/showproblem.php?pid=2159
  5. #include<cstdio>
  6. #include<cstring>
  7. int main()
  8. {
  9. int n,m,f[120][120],s,k,a[120],b[120];
  10. while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF)
  11. {
  12. memset(f,0,sizeof(f));
  13. for(int i=0;i<k;i++) scanf("%d%d",&a[i],&b[i]);
  14. for(int i=0;i<k;i++)
  15. for(int j=1;j<=s;j++)//注意动物的开始数是1
  16. for(int z=b[i];z<=m;z++) f[j][z]=f[j][z]>(f[j-1][z-b[i]]+a[i])?f[j][z]:(f[j-1][z-b[i]]+a[i]);//注意要j-1
  17. if(f[s][m]>=n)
  18. {
  19. for(int i=0;i<=m;++i)
  20. if(f[s][i]>=n)
  21. {
  22. printf("%d\n",m-i);
  23. break;
  24. }
  25. }
  26. else puts("-1");
  27. }
  28. return 0;
  29. }

复杂度

分组背包问题

【简介】

件物品和一个容量为的背包。第件物品的费用是,价值是。这些物品被划分为组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

【代码实现】
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1757
  5. #include<cstdio>
  6. #include<vector>
  7. using namespace std;
  8. struct Node{
  9. int weight,value;
  10. }cnt;
  11. vector<Node> t[101];
  12. int n,m,dp[1001],belong,p;
  13. int main()
  14. {
  15. scanf("%d%d",&m,&n);
  16. for(int i=1;i<=n;i++)
  17. {
  18. scanf("%d%d%d",&cnt.weight,&cnt.value,&belong);
  19. t[belong].push_back(cnt);
  20. }
  21. for(int i=1;i<=100;i++)
  22. if(t[i].size())
  23. {
  24. p=t[i].size();
  25. for(int j=m;j>=0;j--)
  26. for(int k=0;k<p;k++)
  27. if(j>=t[i][k].weight) dp[j]=max(dp[j],dp[j-t[i][k].weight]+t[i][k].value);
  28. }
  29. printf("%d",dp[m]);
  30. return 0;
  31. }

复杂度

依赖背包问题

【简介】

这种背包问题的物品间存在某种“依赖”的关系。也就是说,物品依赖于物品,表示若选物品,则必须选物品。为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品。

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P1064
  2. #include<cstdio>
  3. #include<cstring>
  4. #define max(x,y) (x)>(y)?(x):(y)
  5. struct cas{
  6. int v,p,q;
  7. }a[60],pat[60][60];
  8. int n,m,t[60],V[60][10],P[60][10],cnt[60],f[32000],ans;
  9. int main()
  10. {
  11. scanf("%d%d",&n,&m);
  12. for(int i=1;i<=m;i++)
  13. {
  14. scanf("%d%d%d",&a[i].v,&a[i].p,&a[i].q);//正常的读入
  15. if(a[i].q)//如果这个物品是附件
  16. {
  17. t[a[i].q]++;
  18. pat[a[i].q][t[a[i].q]].v=a[i].v;
  19. pat[a[i].q][t[a[i].q]].p=a[i].p;
  20. pat[a[i].q][t[a[i].q]].q=a[i].q;//把它存在相应的主件的分组中
  21. }
  22. }
  23. for(int i=1;i<=m;i++)//01背包处理
  24. {
  25. if(t[i])//如果当前物品为主件
  26. {
  27. memset(f,-1,sizeof(f));//恰好背包的处理,-1表示不恰好取到此价值
  28. f[0]=0;//恰好背包的处理
  29. for(int j=1;j<=t[i];j++)
  30. for(int k=n-a[i].v;k>=pat[i][j].v;k--)
  31. if(f[k]<f[k-pat[i][j].v]+pat[i][j].v*pat[i][j].p&&f[k-pat[i][j].v]!=-1)//恰好背包的判断
  32. f[k]=f[k-pat[i][j].v]+pat[i][j].v*pat[i][j].p;//很平常的01状态转移
  33. for(int j=0;j<=n-a[i].v;j++)
  34. if(f[j]!=-1)//恰好背包的判断,这种附件组合满足题意
  35. {
  36. cnt[i]++;
  37. V[i][cnt[i]]=j+a[i].v;
  38. P[i][cnt[i]]=f[j]+a[i].v*a[i].p;//把此情况存在主件i的分组中,为分组背包做好处理
  39. }
  40. }
  41. if(!a[i].q)//只买主件
  42. {
  43. cnt[i]++;
  44. V[i][cnt[i]]=a[i].v;
  45. P[i][cnt[i]]=a[i].v*a[i].p;//存储
  46. }
  47. }
  48. memset(f,0,sizeof(f));
  49. for(int i=1;i<=m;i++)
  50. for(int j=n;j>=0;j--)
  51. for(int k=1;k<=cnt[i];k++)
  52. if(j>=V[i][k]) f[j]=max(f[j],f[j-V[i][k]]+P[i][k]);//分组背包的计算
  53. for(int i=0;i<=n;i++) ans=max(ans,f[i]);
  54. printf("%d",ans);//输出
  55. return 0;
  56. }

复杂度

树形动态规划

【例题】

某大学有个职员,编号为。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1352#sub
  5. #include<iostream>
  6. #include<cstdio>
  7. #include<cstring>
  8. using namespace std;
  9. struct node{
  10. int to,next;
  11. }e[6001];
  12. int f[6001][2];
  13. int head[6001],a[6001],r[6001];
  14. int cnt,n;
  15. void ins(int u,int v)
  16. {
  17. e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
  18. }
  19. void dp(int x)
  20. {
  21. f[x][0]=0;
  22. f[x][1]=a[x];
  23. for(int i=head[x];i;i=e[i].next)
  24. {
  25. int v=e[i].to;
  26. dp(v);
  27. f[x][0]+=max(f[v][0],f[v][1]);
  28. f[x][1]+=f[v][0];
  29. }
  30. }
  31. int main()
  32. {
  33. scanf("%d",&n);
  34. for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  35. for(int u=1,v=1;u&&v;)
  36. {
  37. scanf("%d%d",&u,&v);
  38. ins(v,u);
  39. r[u]=1;
  40. }
  41. for(int i=1;i<=n;i++)
  42. if(!r[i])
  43. {
  44. dp(i);
  45. printf("%d",max(f[i][0],f[i][1]));
  46. break;
  47. }
  48. return 0;
  49. }

复杂度

DAG上的动态规划

【例题】

个矩形,每个矩形可以用来描述,表示长和宽。矩形可以嵌套在矩形中当且仅当或者(相当于旋转度)。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. #include<algorithm>
  5. #include<cstdio>
  6. #include<cstring>
  7. struct node{
  8. int x,y;
  9. }juxing[1005];
  10. int cmp(node a,node b)
  11. {
  12. return a.x<b.x||(a.x==b.x&&a.y<b.y);
  13. }
  14. int N,n,ans,maxx,sum[1005];
  15. int main()
  16. {
  17. scanf("%d",&N);
  18. while(N--)
  19. {
  20. scanf("%d",&n);
  21. for(int i=0;i<n;i++)
  22. {
  23. scanf("%d%d",&juxing[i].x,&juxing[i].y);
  24. if(juxing[i].x<juxing[i].y)
  25. {
  26. int temp=juxing[i].x;
  27. juxing[i].x=juxing[i].y;
  28. juxing[i].y=temp;
  29. }
  30. sum[i]=1;
  31. }
  32. std::sort(juxing,juxing+n,cmp);
  33. maxx=1;
  34. for(int i=1;i<n;i++)
  35. {
  36. ans=0;
  37. for(int j=0;j<i;j++)
  38. if(juxing[j].x<juxing[i].x&&juxing[j].y<juxing[i].y)
  39. ans=std::max(sum[j],ans);
  40. sum[i]=ans+1;
  41. maxx=std::max(sum[i],maxx);
  42. }
  43. printf("%d\n",maxx);
  44. }
  45. return 0;
  46. }

复杂度

数位动规

【例题】

杭州人称那些傻乎乎粘嗒嗒的人为(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有的号码。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

【代码实现】

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. int dp[10][3];
  6. void Init()//预处理,算出所有可能
  7. {
  8. memset(dp,0,sizeof(dp));
  9. dp[0][0]=1;
  10. for(int i=1;i<=8;i++)
  11. {
  12. dp[i][0]=dp[i-1][0]*9-dp[i-1][1];//在不含不吉利数62和4的首位分别补除了4的9个数字,减去在2前面补6的个数
  13. dp[i][1]=dp[i-1][0];//在不含不吉利数在首位补2
  14. dp[i][2]=dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1];//各种出现不吉利数的情况
  15. }
  16. }
  17. int Solve(int x)
  18. {
  19. int digit[15];
  20. int cnt=0,tmp=x;
  21. while(tmp)
  22. {
  23. digit[++cnt]=tmp%10;
  24. tmp/=10;
  25. }
  26. digit[cnt+1]=0;
  27. int flag=0,ans=0;
  28. for(int i=cnt;i>0;i--)
  29. {
  30. ans+=digit[i]*dp[i-1][2];//由上位所有非吉利数推导
  31. if(flag)//之前出现非吉利的数字
  32. ans+=digit[i]*dp[i-1][0];
  33. else
  34. {
  35. if(digit[i]>4)//出现4
  36. ans+=dp[i-1][0];
  37. if(digit[i]>6)//出现6
  38. ans+=dp[i-1][1];
  39. if(digit[i+1]==6&&digit[i]>2)//出现62
  40. ans+=dp[i][1];
  41. }
  42. if(digit[i]==4||(digit[i+1]==6&&digit[i]==2))
  43. flag=1;
  44. }
  45. return x-ans;//所有的数减去非吉利的数
  46. }
  47. int main()
  48. {
  49. int a,b;
  50. Init();
  51. while(~scanf("%d%d",&a,&b))
  52. {
  53. if(a==0&&b==0)
  54. break;
  55. printf("%d\n",Solve(b+1)-Solve(a));
  56. }
  57. return 0;
  58. }

复杂度

状态压缩动态规划

【例题】

现有盏灯,以及个按钮。每个按钮可以同时控制这盏灯——按下了第个按钮,对于所有的灯都有一个效果。按下按钮对于第盏灯,是下面中效果之一:
如果,那么当这盏灯开了的时候,把它关上,否则不管;
如果为的话,如果这盏灯是关的,那么把它打开,否则也不管;
如果是,无论这灯是否开,都不管。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。

【代码实现】

  1. #include<iostream>
  2. #include<vector>
  3. #include<queue>
  4. #include<cstdio>
  5. #include<cstring>
  6. using namespace std;
  7. int RD()
  8. {
  9. int out=0,flag=1;
  10. char c=getchar();
  11. while(c<'0'||c>'9')
  12. {
  13. if(c=='-')flag=-1;
  14. c=getchar();
  15. }
  16. while(c>='0'&&c<='9')
  17. {
  18. out=out*10+c-'0';
  19. c=getchar();
  20. }
  21. return flag*out;
  22. }
  23. const int maxn=2048;
  24. int num,m,numd;
  25. struct Node{
  26. int dp,step;
  27. };
  28. int vis[maxn];
  29. int map[maxn][maxn];
  30. void BFS(int n)
  31. {
  32. queue<Node>Q;
  33. Node fir;
  34. fir.step=0,fir.dp=n;//初始状态入队
  35. Q.push(fir);
  36. while(!Q.empty())//BFS
  37. {
  38. Node u=Q.front();
  39. Q.pop();
  40. int pre=u.dp;
  41. for(int i=1;i<=m;i++)//枚举每个操作
  42. {
  43. int now=pre;
  44. for(int j=1;j<=num;j++)
  45. {
  46. if(map[i][j]==1)
  47. {
  48. if((1<<(j-1))&now) now=now^(1<<(j-1));//对状态进行操作
  49. }
  50. else if(map[i][j]==-1) now=((1<<(j-1))|now);//对状态进行操作
  51. }
  52. fir.dp=now,fir.step=u.step+1;//记录步数
  53. if(vis[now]==true)continue;
  54. if(fir.dp==0)//达到目标状态
  55. {
  56. vis[0]=true;//相当于一个标记flag
  57. cout<<fir.step<<endl;//输出
  58. return;//退出函数
  59. }
  60. Q.push(fir);//新状态入队
  61. vis[now]=true;//表示这个状态操作过了(以后在有这个状态就不用试了)
  62. }
  63. }
  64. }
  65. int main()
  66. {
  67. num=RD();
  68. m=RD();
  69. int n=(1<<(num))-1;
  70. for(int i=1;i<=m;i++)
  71. for(int j=1;j<=num;j++)
  72. map[i][j]=RD();
  73. BFS(n);
  74. if(vis[0]==false) puts("-1");
  75. return 0;
  76. }

数据结构

队列

【简介】

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3187/
  5. #include<cstdio>
  6. #include<queue>
  7. using namespace std;
  8. queue<int> q;
  9. int n,opt,k;
  10. int main()
  11. {
  12. scanf("%d",&n);
  13. for(int i=1;i<=n;i++)
  14. {
  15. scanf("%d",&opt);
  16. if(opt==1) scanf("%d",&k),q.push(k);
  17. else if(opt==2) q.pop();
  18. else printf("%d\n",q.front());
  19. }
  20. return 0;
  21. }

复杂度(入队,出队,访问队首)

【简介】

栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3139/
  5. #include<cstdio>
  6. #include<stack>
  7. using namespace std;
  8. int n,opt,k;
  9. stack<int> s;
  10. int main()
  11. {
  12. scanf("%d",&n);
  13. for(int i=1;i<=n;i++)
  14. {
  15. scanf("%d",&opt);
  16. if(opt==1) scanf("%d",&k),s.push(k);
  17. else if(opt==2) s.pop();
  18. else printf("%d\n",s.top());
  19. }
  20. return 0;
  21. }

复杂度(入栈,出栈,访问栈顶)

【简介】

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。

【代码实现】

二叉堆
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3378#sub
  5. #include<cstdio>
  6. #include<queue>
  7. #include<cctype>
  8. using namespace std;
  9. priority_queue<int,vector<int>,greater<int> > q;
  10. int m,a,b;
  11. int readin()
  12. {
  13. int res=0;char ch=0;bool XX=false;
  14. for(;!isdigit(ch);ch=getchar())
  15. if(ch=='-') XX=true;
  16. for(;isdigit(ch);ch=getchar()) res=(res<<3)+(res<<1)+ch-'0';
  17. return (XX?-res:res);
  18. }
  19. void writeout(int res)
  20. {
  21. if(res<0) {putchar('-');res=-res;}
  22. if(res>9) writeout(res/10);
  23. putchar(res%10+'0');
  24. }
  25. int main()
  26. {
  27. m=readin();
  28. for(int i=1;i<=m;i++)
  29. {
  30. a=readin();
  31. if(a==1)
  32. {
  33. b=readin();
  34. q.push(b);
  35. }
  36. else if(a==2) writeout(q.top()),putchar('\n');
  37. else q.pop();
  38. }
  39. return 0;
  40. }
*斐波那契堆
  1. /**
  2. * C++: 斐波那契堆
  3. *
  4. * @author skywang
  5. * @date 2014/04/06
  6. */
  7. #ifndef _FIBONACCI_TREE_HPP_
  8. #define _FIBONACCI_TREE_HPP_
  9. #include <iomanip>
  10. #include <iostream>
  11. #include <cstdlib>
  12. #include <cmath>
  13. using namespace std;
  14. template <class T>
  15. class FibNode {
  16. public:
  17. T key; // 关键字(键值)
  18. int degree; // 度数
  19. FibNode<T> *left; // 左兄弟
  20. FibNode<T> *right; // 右兄弟
  21. FibNode<T> *child; // 第一个孩子节点
  22. FibNode<T> *parent; // 父节点
  23. bool marked; // 是否被删除第一个孩子
  24. FibNode(T value):key(value), degree(0), marked(false),
  25. left(NULL),right(NULL),child(NULL),parent(NULL) {
  26. key = value;
  27. degree = 0;
  28. marked = false;
  29. left = this;
  30. right = this;
  31. parent = NULL;
  32. child = NULL;
  33. }
  34. };
  35. template <class T>
  36. class FibHeap {
  37. private:
  38. int keyNum; // 堆中节点的总数
  39. int maxDegree; // 最大度
  40. FibNode<T> *min; // 最小节点(某个最小堆的根节点)
  41. FibNode<T> **cons; // 最大度的内存区域
  42. public:
  43. FibHeap();
  44. ~FibHeap();
  45. // 新建key对应的节点,并将其插入到斐波那契堆中
  46. void insert(T key);
  47. // 移除斐波那契堆中的最小节点
  48. void removeMin();
  49. // 将other合并到当前堆中
  50. void combine(FibHeap<T> *other);
  51. // 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
  52. bool minimum(T *pkey);
  53. // 将斐波那契堆中键值oldkey更新为newkey
  54. void update(T oldkey, T newkey);
  55. // 删除键值为key的节点
  56. void remove(T key);
  57. // 斐波那契堆中是否包含键值key
  58. bool contains(T key);
  59. // 打印斐波那契堆
  60. void print();
  61. // 销毁
  62. void destroy();
  63. private:
  64. // 将node从双链表移除
  65. void removeNode(FibNode<T> *node);
  66. // 将node堆结点加入root结点之前(循环链表中)
  67. void addNode(FibNode<T> *node, FibNode<T> *root);
  68. // 将双向链表b链接到双向链表a的后面
  69. void catList(FibNode<T> *a, FibNode<T> *b);
  70. // 将节点node插入到斐波那契堆中
  71. void insert(FibNode<T> *node);
  72. // 将"堆的最小结点"从根链表中移除,
  73. FibNode<T>* extractMin();
  74. // 将node链接到root根结点
  75. void link(FibNode<T>* node, FibNode<T>* root);
  76. // 创建consolidate所需空间
  77. void makeCons();
  78. // 合并斐波那契堆的根链表中左右相同度数的树
  79. void consolidate();
  80. // 修改度数
  81. void renewDegree(FibNode<T> *parent, int degree);
  82. // 将node从父节点parent的子链接中剥离出来,并使node成为"堆的根链表"中的一员。
  83. void cut(FibNode<T> *node, FibNode<T> *parent);
  84. // 对节点node进行"级联剪切"
  85. void cascadingCut(FibNode<T> *node) ;
  86. // 将斐波那契堆中节点node的值减少为key
  87. void decrease(FibNode<T> *node, T key);
  88. // 将斐波那契堆中节点node的值增加为key
  89. void increase(FibNode<T> *node, T key);
  90. // 更新斐波那契堆的节点node的键值为key
  91. void update(FibNode<T> *node, T key);
  92. // 在最小堆root中查找键值为key的节点
  93. FibNode<T>* search(FibNode<T> *root, T key);
  94. // 在斐波那契堆中查找键值为key的节点
  95. FibNode<T>* search(T key);
  96. // 删除结点node
  97. void remove(FibNode<T> *node);
  98. // 销毁斐波那契堆
  99. void destroyNode(FibNode<T> *node);
  100. // 打印"斐波那契堆"
  101. void print(FibNode<T> *node, FibNode<T> *prev, int direction);
  102. };
  103. /*
  104. * 构造函数
  105. */
  106. template <class T>
  107. FibHeap<T>::FibHeap()
  108. {
  109. keyNum = 0;
  110. maxDegree = 0;
  111. min = NULL;
  112. cons = NULL;
  113. }
  114. /*
  115. * 析构函数
  116. */
  117. template <class T>
  118. FibHeap<T>::~FibHeap()
  119. {
  120. }
  121. /*
  122. * 将node从双链表移除
  123. */
  124. template <class T>
  125. void FibHeap<T>::removeNode(FibNode<T> *node)
  126. {
  127. node->left->right = node->right;
  128. node->right->left = node->left;
  129. }
  130. /*
  131. * 将node堆结点加入root结点之前(循环链表中)
  132. * a …… root
  133. * a …… node …… root
  134. */
  135. template <class T>
  136. void FibHeap<T>::addNode(FibNode<T> *node, FibNode<T> *root)
  137. {
  138. node->left = root->left;
  139. root->left->right = node;
  140. node->right = root;
  141. root->left = node;
  142. }
  143. /*
  144. * 将节点node插入到斐波那契堆中
  145. */
  146. template <class T>
  147. void FibHeap<T>::insert(FibNode<T> *node)
  148. {
  149. if (keyNum == 0)
  150. min = node;
  151. else
  152. {
  153. addNode(node, min);
  154. if (node->key < min->key)
  155. min = node;
  156. }
  157. keyNum++;
  158. }
  159. /*
  160. * 新建键值为key的节点,并将其插入到斐波那契堆中
  161. */
  162. template <class T>
  163. void FibHeap<T>::insert(T key)
  164. {
  165. FibNode<T> *node;
  166. node = new FibNode<T>(key);
  167. if (node == NULL)
  168. return ;
  169. insert(node);
  170. }
  171. /*
  172. * 将双向链表b链接到双向链表a的后面
  173. *
  174. * 注意: 此处a和b都是双向链表
  175. */
  176. template <class T>
  177. void FibHeap<T>::catList(FibNode<T> *a, FibNode<T> *b)
  178. {
  179. FibNode<T> *tmp;
  180. tmp = a->right;
  181. a->right = b->right;
  182. b->right->left = a;
  183. b->right = tmp;
  184. tmp->left = b;
  185. }
  186. /*
  187. * 将other合并到当前堆中
  188. */
  189. template <class T>
  190. void FibHeap<T>::combine(FibHeap<T> *other)
  191. {
  192. if (other==NULL)
  193. return ;
  194. if(other->maxDegree > this->maxDegree)
  195. swap(*this, *other);
  196. if((this->min) == NULL) // this无"最小节点"
  197. {
  198. this->min = other->min;
  199. this->keyNum = other->keyNum;
  200. free(other->cons);
  201. delete other;
  202. }
  203. else if((other->min) == NULL) // this有"最小节点" && other无"最小节点"
  204. {
  205. free(other->cons);
  206. delete other;
  207. } // this有"最小节点" && other有"最小节点"
  208. else
  209. {
  210. // 将"other中根链表"添加到"this"中
  211. catList(this->min, other->min);
  212. if (this->min->key > other->min->key)
  213. this->min = other->min;
  214. this->keyNum += other->keyNum;
  215. free(other->cons);
  216. delete other;
  217. }
  218. }
  219. /*
  220. * 将"堆的最小结点"从根链表中移除,
  221. * 这意味着"将最小节点所属的树"从堆中移除!
  222. */
  223. template <class T>
  224. FibNode<T>* FibHeap<T>::extractMin()
  225. {
  226. FibNode<T> *p = min;
  227. if (p == p->right)
  228. min = NULL;
  229. else
  230. {
  231. removeNode(p);
  232. min = p->right;
  233. }
  234. p->left = p->right = p;
  235. return p;
  236. }
  237. /*
  238. * 将node链接到root根结点
  239. */
  240. template <class T>
  241. void FibHeap<T>::link(FibNode<T>* node, FibNode<T>* root)
  242. {
  243. // 将node从双链表中移除
  244. removeNode(node);
  245. // 将node设为root的孩子
  246. if (root->child == NULL)
  247. root->child = node;
  248. else
  249. addNode(node, root->child);
  250. node->parent = root;
  251. root->degree++;
  252. node->marked = false;
  253. }
  254. /*
  255. * 创建consolidate所需空间
  256. */
  257. template <class T>
  258. void FibHeap<T>::makeCons()
  259. {
  260. int old = maxDegree;
  261. // 计算log2(keyNum),"+1"意味着向上取整!
  262. // ex. log2(13) = 3,向上取整为3+1=4。
  263. maxDegree = (log(keyNum)/log(2.0)) + 1;
  264. if (old >= maxDegree)
  265. return ;
  266. // 因为度为maxDegree可能被合并,所以要maxDegree+1
  267. cons = (FibNode<T> **)realloc(cons,
  268. sizeof(FibHeap<T> *) * (maxDegree + 1));
  269. }
  270. /*
  271. * 合并斐波那契堆的根链表中左右相同度数的树
  272. */
  273. template <class T>
  274. void FibHeap<T>::consolidate()
  275. {
  276. int i, d, D;
  277. FibNode<T> *x, *y, *tmp;
  278. makeCons();//开辟哈希所用空间
  279. D = maxDegree + 1;
  280. for (i = 0; i < D; i++)
  281. cons[i] = NULL;
  282. // 合并相同度的根节点,使每个度数的树唯一
  283. while (min != NULL)
  284. {
  285. x = extractMin(); // 取出堆中的最小树(最小节点所在的树)
  286. d = x->degree; // 获取最小树的度数
  287. // cons[d] != NULL,意味着有两棵树(x和y)的"度数"相同。
  288. while (cons[d] != NULL)
  289. {
  290. y = cons[d]; // y是"与x的度数相同的树"
  291. if (x->key > y->key) // 保证x的键值比y小
  292. swap(x, y);
  293. link(y, x); // 将y链接到x中
  294. cons[d] = NULL;
  295. d++;
  296. }
  297. cons[d] = x;
  298. }
  299. min = NULL;
  300. // 将cons中的结点重新加到根表中
  301. for (i=0; i<D; i++)
  302. {
  303. if (cons[i] != NULL)
  304. {
  305. if (min == NULL)
  306. min = cons[i];
  307. else
  308. {
  309. addNode(cons[i], min);
  310. if ((cons[i])->key < min->key)
  311. min = cons[i];
  312. }
  313. }
  314. }
  315. }
  316. /*
  317. * 移除最小节点
  318. */
  319. template <class T>
  320. void FibHeap<T>::removeMin()
  321. {
  322. if (min==NULL)
  323. return ;
  324. FibNode<T> *child = NULL;
  325. FibNode<T> *m = min;
  326. // 将min每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中
  327. while (m->child != NULL)
  328. {
  329. child = m->child;
  330. removeNode(child);
  331. if (child->right == child)
  332. m->child = NULL;
  333. else
  334. m->child = child->right;
  335. addNode(child, min);
  336. child->parent = NULL;
  337. }
  338. // 将m从根链表中移除
  339. removeNode(m);
  340. // 若m是堆中唯一节点,则设置堆的最小节点为NULL;
  341. // 否则,设置堆的最小节点为一个非空节点(m->right),然后再进行调节。
  342. if (m->right == m)
  343. min = NULL;
  344. else
  345. {
  346. min = m->right;
  347. consolidate();
  348. }
  349. keyNum--;
  350. delete m;
  351. }
  352. /*
  353. * 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
  354. */
  355. template <class T>
  356. bool FibHeap<T>::minimum(T *pkey)
  357. {
  358. if (min==NULL || pkey==NULL)
  359. return false;
  360. *pkey = min->key;
  361. return true;
  362. }
  363. /*
  364. * 修改度数
  365. */
  366. template <class T>
  367. void FibHeap<T>::renewDegree(FibNode<T> *parent, int degree)
  368. {
  369. parent->degree -= degree;
  370. if (parent-> parent != NULL)
  371. renewDegree(parent->parent, degree);
  372. }
  373. /*
  374. * 将node从父节点parent的子链接中剥离出来,
  375. * 并使node成为"堆的根链表"中的一员。
  376. */
  377. template <class T>
  378. void FibHeap<T>::cut(FibNode<T> *node, FibNode<T> *parent)
  379. {
  380. removeNode(node);
  381. renewDegree(parent, node->degree);
  382. // node没有兄弟
  383. if (node == node->right)
  384. parent->child = NULL;
  385. else
  386. parent->child = node->right;
  387. node->parent = NULL;
  388. node->left = node->right = node;
  389. node->marked = false;
  390. // 将"node所在树"添加到"根链表"中
  391. addNode(node, min);
  392. }
  393. /*
  394. * 对节点node进行"级联剪切"
  395. *
  396. * 级联剪切:如果减小后的结点破坏了最小堆性质,
  397. * 则把它切下来(即从所在双向链表中删除,并将
  398. * 其插入到由最小树根节点形成的双向链表中),
  399. * 然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝
  400. */
  401. template <class T>
  402. void FibHeap<T>::cascadingCut(FibNode<T> *node)
  403. {
  404. FibNode<T> *parent = node->parent;
  405. if (parent != NULL)
  406. {
  407. if (node->marked == false)
  408. node->marked = true;
  409. else
  410. {
  411. cut(node, parent);
  412. cascadingCut(parent);
  413. }
  414. }
  415. }
  416. /*
  417. * 将斐波那契堆中节点node的值减少为key
  418. */
  419. template <class T>
  420. void FibHeap<T>::decrease(FibNode<T> *node, T key)
  421. {
  422. FibNode<T> *parent;
  423. if (min==NULL ||node==NULL)
  424. return ;
  425. if ( key>=node->key)
  426. {
  427. cout << "decrease failed: the new key(" << key <<") "
  428. << "is no smaller than current key(" << node->key <<")" << endl;
  429. return ;
  430. }
  431. node->key = key;
  432. parent = node->parent;
  433. if (parent!=NULL && node->key < parent->key)
  434. {
  435. // 将node从父节点parent中剥离出来,并将node添加到根链表中
  436. cut(node, parent);
  437. cascadingCut(parent);
  438. }
  439. // 更新最小节点
  440. if (node->key < min->key)
  441. min = node;
  442. }
  443. /*
  444. * 将斐波那契堆中节点node的值增加为key
  445. */
  446. template <class T>
  447. void FibHeap<T>::increase(FibNode<T> *node, T key)
  448. {
  449. FibNode<T> *child, *parent, *right;
  450. if (min==NULL ||node==NULL)
  451. return ;
  452. if (key <= node->key)
  453. {
  454. cout << "increase failed: the new key(" << key <<") "
  455. << "is no greater than current key(" << node->key <<")" << endl;
  456. return ;
  457. }
  458. // 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中
  459. while (node->child != NULL)
  460. {
  461. child = node->child;
  462. removeNode(child); // 将child从node的子链表中删除
  463. if (child->right == child)
  464. node->child = NULL;
  465. else
  466. node->child = child->right;
  467. addNode(child, min); // 将child添加到根链表中
  468. child->parent = NULL;
  469. }
  470. node->degree = 0;
  471. node->key = key;
  472. // 如果node不在根链表中,
  473. // 则将node从父节点parent的子链接中剥离出来,
  474. // 并使node成为"堆的根链表"中的一员,
  475. // 然后进行"级联剪切"
  476. // 否则,则判断是否需要更新堆的最小节点
  477. parent = node->parent;
  478. if(parent != NULL)
  479. {
  480. cut(node, parent);
  481. cascadingCut(parent);
  482. }
  483. else if(min == node)
  484. {
  485. right = node->right;
  486. while(right != node)
  487. {
  488. if(node->key > right->key)
  489. min = right;
  490. right = right->right;
  491. }
  492. }
  493. }
  494. /*
  495. * 更新斐波那契堆的节点node的键值为key
  496. */
  497. template <class T>
  498. void FibHeap<T>::update(FibNode<T> *node, T key)
  499. {
  500. if(key < node->key)
  501. decrease(node, key);
  502. else if(key > node->key)
  503. increase(node, key);
  504. else
  505. cout << "No need to update!!!" << endl;
  506. }
  507. template <class T>
  508. void FibHeap<T>::update(T oldkey, T newkey)
  509. {
  510. FibNode<T> *node;
  511. node = search(oldkey);
  512. if (node!=NULL)
  513. update(node, newkey);
  514. }
  515. /*
  516. * 在最小堆root中查找键值为key的节点
  517. */
  518. template <class T>
  519. FibNode<T>* FibHeap<T>::search(FibNode<T> *root, T key)
  520. {
  521. FibNode<T> *t = root; // 临时节点
  522. FibNode<T> *p = NULL; // 要查找的节点
  523. if (root==NULL)
  524. return root;
  525. do
  526. {
  527. if (t->key == key)
  528. {
  529. p = t;
  530. break;
  531. }
  532. else
  533. {
  534. if ((p = search(t->child, key)) != NULL)
  535. break;
  536. }
  537. t = t->right;
  538. } while (t != root);
  539. return p;
  540. }
  541. /*
  542. * 在斐波那契堆中查找键值为key的节点
  543. */
  544. template <class T>
  545. FibNode<T>* FibHeap<T>::search(T key)
  546. {
  547. if (min==NULL)
  548. return NULL;
  549. return search(min, key);
  550. }
  551. /*
  552. * 在斐波那契堆中是否存在键值为key的节点。
  553. * 存在返回true,否则返回false。
  554. */
  555. template <class T>
  556. bool FibHeap<T>::contains(T key)
  557. {
  558. return search(key)!=NULL ? true: false;
  559. }
  560. /*
  561. * 删除结点node
  562. */
  563. template <class T>
  564. void FibHeap<T>::remove(FibNode<T> *node)
  565. {
  566. T m = min->key-1;
  567. decrease(node, m-1);
  568. removeMin();
  569. }
  570. template <class T>
  571. void FibHeap<T>::remove(T key)
  572. {
  573. FibNode<T> *node;
  574. if (min==NULL)
  575. return ;
  576. node = search(key);
  577. if (node==NULL)
  578. return ;
  579. remove(node);
  580. }
  581. /*
  582. * 销毁斐波那契堆
  583. */
  584. template <class T>
  585. void FibHeap<T>::destroyNode(FibNode<T> *node)
  586. {
  587. FibNode<T> *start = node;
  588. if(node == NULL)
  589. return;
  590. do {
  591. destroyNode(node->child);
  592. // 销毁node,并将node指向下一个
  593. node = node->right;
  594. delete node->left;
  595. } while(node != start);
  596. }
  597. template <class T>
  598. void FibHeap<T>::destroy()
  599. {
  600. destroyNode(min);
  601. free(cons);
  602. }
  603. /*
  604. * 打印"斐波那契堆"
  605. *
  606. * 参数说明:
  607. * node -- 当前节点
  608. * prev -- 当前节点的前一个节点(父节点or兄弟节点)
  609. * direction -- 1,表示当前节点是一个左孩子;
  610. * 2,表示当前节点是一个兄弟节点。
  611. */
  612. template <class T>
  613. void FibHeap<T>::print(FibNode<T> *node, FibNode<T> *prev, int direction)
  614. {
  615. FibNode<T> *start=node;
  616. if (node==NULL)
  617. return ;
  618. do
  619. {
  620. if (direction == 1)
  621. cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s child" << endl;
  622. else
  623. cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s next" << endl;
  624. if (node->child != NULL)
  625. print(node->child, node, 1);
  626. // 兄弟节点
  627. prev = node;
  628. node = node->right;
  629. direction = 2;
  630. } while(node != start);
  631. }
  632. template <class T>
  633. void FibHeap<T>::print()
  634. {
  635. int i=0;
  636. FibNode<T> *p;
  637. if (min==NULL)
  638. return ;
  639. cout << "== 斐波那契堆的详细信息: ==" << endl;
  640. p = min;
  641. do {
  642. i++;
  643. cout << setw(2) << i << ". " << setw(4) << p->key << "(" << p->degree << ") is root" << endl;
  644. print(p->child, p, 1);
  645. p = p->right;
  646. } while (p != min);
  647. cout << endl;
  648. }
  649. #endif
*左偏堆
  1. //problemID:https://www.lydsy.com/JudgeOnline/problem.php?id=1455
  2. #include <iostream>
  3. #include <cstdio>
  4. using namespace std;
  5. const int N=1000001;
  6. int n,m; int a[N]; bool die[N];
  7. int l[N],r[N],fa[N],dep[N];
  8. int readin()
  9. {
  10. int x=0,f=1; char ch=getchar();
  11. while(ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
  12. while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
  13. return x*f;
  14. }
  15. int find(int x)
  16. {
  17. if (fa[x]!=x)
  18. fa[x]=find(fa[x]);
  19. return fa[x];
  20. }
  21. void read()
  22. {
  23. n=readin();
  24. for (int i=1;i<=n;i++)
  25. a[i]=readin(),fa[i]=i;
  26. m=readin();
  27. return;
  28. }
  29. int merge(int x,int y)
  30. {
  31. if (!x) return y;
  32. if (!y) return x;
  33. if (a[x]>a[y]) swap(x,y);
  34. r[x]=merge(r[x],y);
  35. if (dep[r[x]]>dep[l[x]])
  36. swap(l[x],r[x]);
  37. dep[x]=dep[r[x]]+1;
  38. return x;
  39. }
  40. void work()
  41. {
  42. char ch[10]; int x,y;
  43. for (int i=1;i<=m;i++)
  44. {
  45. scanf("%s",ch);
  46. if (ch[0]=='M') {
  47. x=readin(),y=readin();
  48. if (die[x]||die[y]) continue;
  49. int f1=find(x),f2=find(y);
  50. if (f1!=f2) {
  51. int t=merge(f1,f2);
  52. fa[f2]=fa[f1]=t;
  53. }
  54. }
  55. else {
  56. x=readin();
  57. if (die[x]) printf("0\n");
  58. else {
  59. int f1=find(x); die[f1]=1;
  60. printf("%d\n",a[f1]);
  61. fa[f1]=merge(l[f1],r[f1]);
  62. fa[fa[f1]]=fa[f1];
  63. }
  64. }
  65. }
  66. return;
  67. }
  68. int main()
  69. {
  70. read();
  71. work();
  72. return 0;
  73. }
*配对堆
  1. //problemID:https://www.luogu.org/problemnew/show/P3371
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. const int M=2e6+5;
  5. int n,root,id,tot,top,val[M],head[M],nxt[M],to[M],dad[M];
  6. queue<int>dui;
  7. void add(int x,int y)
  8. {
  9. nxt[++id]=head[x],head[x]=id,to[id]=y,dad[y]=x;
  10. }
  11. int _new(int x)
  12. {
  13. val[++tot]=x;
  14. return tot;
  15. }
  16. int merge(int x,int y)
  17. {
  18. if(!x||!y)return x+y;
  19. if(val[x]<val[y])
  20. {
  21. add(x,y);
  22. return x;
  23. }
  24. else
  25. {
  26. add(y,x);
  27. return y;
  28. }
  29. }
  30. void pop()
  31. {
  32. int t;
  33. for(int i=head[root]; i; i=nxt[i])if(dad[to[i]]==root)dui.push(to[i]),dad[to[i]]=0;
  34. while(dui.size()>1)
  35. {
  36. t=dui.front();
  37. dui.pop();
  38. dui.push(merge(dui.front(),t));
  39. dui.pop();
  40. }
  41. if(dui.size())root=dui.front(),dui.pop();
  42. else root=0;
  43. }
  44. int main()
  45. {
  46. scanf("%d",&n);
  47. int op,x;
  48. for(int i=1; i<=n; ++i)
  49. {
  50. scanf("%d",&op);
  51. if(op==1)scanf("%d",&x),root=merge(_new(x),root);
  52. else if(op==2)printf("%d\n",val[root]);
  53. else pop();
  54. }
  55. }

并查集

【简介】

并查集,在一些有个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

【代码实现】

朴素并查集
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3367
  5. #include<cstdio>
  6. #define N 10001
  7. int n,fa[N],m;
  8. void init()
  9. {
  10. for(register int i=1;i<=n;i++) fa[i]=i;
  11. return;
  12. }
  13. int Find(int x)
  14. {
  15. while(x!=fa[x]) x=fa[x];
  16. return x;
  17. }
  18. void MergeSet(int x,int y)
  19. {
  20. int Root1=Find(x),Root2=Find(y);
  21. if(Root1!=Root2) fa[Root1]=Root2;
  22. return;
  23. }
  24. bool Query(int x,int y)
  25. {
  26. return Find(x)==Find(y);
  27. }
  28. int main()
  29. {
  30. scanf("%d%d",&n,&m);
  31. init();
  32. int opt,x,y;
  33. for(int i=1;i<=m;i++)
  34. {
  35. scanf("%d%d%d",&opt,&x,&y);
  36. if(opt==1) MergeSet(x,y);
  37. else puts(Query(x,y)?"Y":"N");
  38. }
  39. return 0;
  40. }
  41. /*
  42. 路径压缩Find()函数:
  43. int Find(int x)
  44. {
  45. return x==fa[x]?x:fa[x]=Find(fa[x]);
  46. }
  47. 随机合并版MergeSet()函数:
  48. void MergeSet(int x,int y)
  49. {
  50. srand(time(NULL));
  51. int Root1=Find(x),Root2=Find(y);
  52. if(Root1!=Root2)
  53. {
  54. if(rand()&1) fa[Root1]=Root2;
  55. else fa[Root2]=Root1;
  56. }
  57. return;
  58. }
  59. 按秩合并版MergeSet()函数:
  60. void MergeSet(int x,int y)
  61. {
  62. int Root1=Find(x),Root2=Find(y);
  63. if(Root1!=Root2)
  64. {
  65. if(rank[Root1]<=rank[Root2]) fa[Root1]=y,rank[Root2]=max(rank[Root2],rank[Root1]+1);
  66. else fa[Root2]=Root1,rank[Root1]=max(rank[Root1],rank[Root2]+1);
  67. }
  68. }
  69. */

复杂度:路径压缩+按秩合并:,其他:

*可持久化并查集
  1. //problemID:https://www.lydsy.com/JudgeOnline/problem.php?id=3674
  2. #include<iostream>
  3. #include<string>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<cstdio>
  7. #include<cstdlib>
  8. #include<stdio.h>
  9. #include<algorithm>
  10. using namespace std;
  11. const int maxn=200100;
  12. const int maxl=20;
  13. struct data
  14. {
  15. int lson,rson,id,val,_Size;
  16. } tree[maxn+2*maxn*maxl];
  17. int Root[maxn];
  18. int cur;
  19. int w[maxn];
  20. int n,m,lt,nt,lans;
  21. void Build(int x)
  22. {
  23. tree[x].id=x;
  24. tree[x].val=x;
  25. tree[x]._Size=1;
  26. int left=x<<1;
  27. if (left<=n)
  28. {
  29. tree[x].lson=left;
  30. Build(left);
  31. }
  32. int right=left|1;
  33. if (right<=n)
  34. {
  35. tree[x].rson=right;
  36. Build(right);
  37. }
  38. }
  39. data Query(int root,int L,int x)
  40. {
  41. if (x==L) return tree[root];
  42. if (x&(1<<(w[x]-w[L]-1))) return Query(tree[root].rson,L*2+1,x);
  43. else return Query(tree[root].lson,L*2,x);
  44. }
  45. data Find_fa(int x)
  46. {
  47. data y=Query(Root[lt],1,x);
  48. while (y.id!=y.val) y=Query(Root[lt],1,y.val);
  49. return y;
  50. }
  51. void Update(int root,int L,int x,int nid,int v)
  52. {
  53. if (L==x)
  54. {
  55. if (nid==0) tree[root].val=v;
  56. else tree[root]._Size=v;
  57. return;
  58. }
  59. if (x&(1<<(w[x]-w[L]-1)))
  60. {
  61. data temp=tree[ tree[root].rson ];
  62. tree[root].rson=++cur;
  63. tree[cur]=temp;
  64. Update(cur,L*2+1,x,nid,v);
  65. }
  66. else
  67. {
  68. data temp=tree[ tree[root].lson ];
  69. tree[root].lson=++cur;
  70. tree[cur]=temp;
  71. Update(cur,L*2,x,nid,v);
  72. }
  73. }
  74. void Add(int x,int y)
  75. {
  76. data fx=Find_fa(x);
  77. data fy=Find_fa(y);
  78. if (fx.val==fy.val)
  79. {
  80. Root[nt]=Root[lt];
  81. return;
  82. }
  83. if (fx._Size>fy._Size) swap(fx,fy);
  84. Root[nt]=++cur;
  85. tree[cur]=tree[ Root[lt] ];
  86. Update(cur,1,fx.id,0,fy.id);
  87. tree[++cur]=tree[ Root[nt] ];
  88. Root[nt]=cur;
  89. Update(cur,1,fy.id,1,fx._Size+fy._Size);
  90. }
  91. int main()
  92. {
  93. freopen("c.in","r",stdin);
  94. freopen("c.out","w",stdout);
  95. scanf("%d%d",&n,&m);
  96. Build(1);
  97. Root[1]=1;
  98. cur=n;
  99. lt=1,nt=1;
  100. lans=0;
  101. w[1]=1;
  102. for (int i=2; i<maxn; i++) w[i]=w[i/2]+1;
  103. /*for (int i=1; i<=n; i++)
  104. {
  105. data temp=Find_fa(i);
  106. printf("%d ",temp.val);
  107. }
  108. printf("\n");*/
  109. for (int i=1; i<=m; i++)
  110. {
  111. int op;
  112. scanf("%d",&op);
  113. if (op==1)
  114. {
  115. int a,b;
  116. scanf("%d%d",&a,&b);
  117. a=a^lans;
  118. b=b^lans;
  119. nt++;
  120. Add(a,b);
  121. lt=nt;
  122. }
  123. if (op==2)
  124. {
  125. int k;
  126. scanf("%d",&k);
  127. k=k^lans;
  128. lt=k+1;
  129. nt++;
  130. Root[nt]=Root[lt];
  131. lt=nt;
  132. }
  133. if (op==3)
  134. {
  135. int a,b;
  136. scanf("%d%d",&a,&b);
  137. a=a^lans;
  138. b=b^lans;
  139. //printf("%d %d\n",a,b);
  140. data fa=Find_fa(a);
  141. data fb=Find_fa(b);
  142. bool f=(fa.val==fb.val);
  143. printf("%d\n",f);
  144. lans=f;
  145. nt++;
  146. Root[nt]=Root[lt];
  147. lt=nt;
  148. }
  149. /*for (int i=1; i<=n; i++)
  150. {
  151. data temp=Find_fa(i);
  152. printf("%d ",temp.val);
  153. }
  154. printf("\n");*/
  155. }
  156. return 0;
  157. }

单调队列

【简介】

单调队列,即单调递减或单调递增的队列。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P1886
  5. #include<cstdio>
  6. #include<deque>
  7. #include<cctype>
  8. using namespace std;
  9. inline char getcha(){
  10. static char buf[100000],*p1=buf,*p2=buf;
  11. return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
  12. }
  13. int readin()
  14. {
  15. int res=0;char ch=getcha();bool XX=false;
  16. for(;!isdigit(ch);ch=getcha())
  17. (ch=='-') && (XX=true);
  18. for(;isdigit(ch);ch=getcha())
  19. res=(res<<3)+(res<<1)+(ch^48);
  20. return XX?-res:res;
  21. }
  22. void writeout(int x)
  23. {
  24. if(x<0) putchar('-'),x=-x;
  25. if(x>9) writeout(x/10);
  26. putchar(x%10+48);
  27. }
  28. #define N 1000001
  29. int a[N],n,k,head,tail;
  30. struct node{
  31. int x,p;
  32. node(){}
  33. node(int xx,int pp){x=xx;p=pp;}
  34. }list[N],List[N];
  35. int main()
  36. {
  37. n=readin();k=readin();
  38. for(int i=1;i<=n;i++) a[i]=readin();
  39. head=1,tail=0;
  40. for(int i=1;i<=n;i++)
  41. {
  42. while(head<=tail&&List[tail].x>=a[i]) tail--;
  43. List[++tail]=node(a[i],i);
  44. while(i-List[head].p>=k) head++;
  45. if(i>=k) writeout(List[head].x),putchar(i==n?'\n':' ');
  46. }
  47. head=1,tail=0;
  48. for(int i=1;i<=n;i++)
  49. {
  50. while(head<=tail&&list[tail].x<=a[i]) tail--;
  51. list[++tail]=node(a[i],i);
  52. while(i-list[head].p>=k) head++;
  53. if(i>=k) writeout(list[head].x),putchar(i==n?'\n':' ');
  54. }
  55. return 0;
  56. }

复杂度

单调栈

【简介】

单调递增或单调减的栈,跟单调队列差不多,但是只用到它的一端,利用它可以用来解决一些ACM/ICPC和OI的题目。

【代码实现】

  1. //problemID:http://poj.org/problem?id=3250
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<stack>
  5. using namespace std;
  6. int main()
  7. {
  8. int i,n,top,a[80010]; //top指向栈顶元素
  9. long long ans; //存储最终结果
  10. stack<int> st; //st为栈,每次入栈的是每头牛的位置
  11. while(~scanf("%d",&n))
  12. {
  13. while(!st.empty()) st.pop(); //清空栈
  14. for(i=0;i<n;i++) scanf("%d",&a[i]);
  15. a[n]=2147483647; //将最后一个元素设为最大值
  16. ans=0;
  17. for(i=0;i<=n;i++)
  18. {
  19. if(st.empty()||a[i]<a[st.top()])
  20. { //如果栈为空或入栈元素小于栈顶元素,则入栈
  21. st.push(i);
  22. }
  23. else
  24. {
  25. while(!st.empty()&&a[i]>=a[st.top()])
  26. { //如果栈不为空并且栈顶元素不大于入栈元素,则将栈顶元素出栈
  27. top=st.top(); //获取栈顶元素
  28. st.pop(); //栈顶元素出栈
  29. //这时候也就找到了第一个比栈顶元素大的元素
  30. //计算这之间牛的个数,为下标之差-1
  31. ans+=(i-top-1);
  32. }
  33. st.push(i); //当所有破坏栈的单调性的元素都出栈后,将当前元素入栈
  34. }
  35. }
  36. cout<<ans;
  37. }
  38. return 0;
  39. }

复杂度

线段树

【简介】

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为。而未优化的空间复杂度为,实际应用时一般还要开的数组以免越界,因此有时需要离散化让空间压缩。

【代码实现】

朴素线段树
  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3372
  5. #include<cstdio>
  6. #define int long long
  7. #define N 100001
  8. struct Tree{
  9. int lazy,sum;
  10. }tree[N<<2];
  11. int a[N],n,m,x,y,opt,k;
  12. void build(int l,int r,int node)
  13. {
  14. if(l==r)
  15. {
  16. tree[node].sum=a[l];
  17. return;
  18. }
  19. int mid=(l+r)>>1;
  20. build(l,mid,node<<1);
  21. build(mid+1,r,node<<1|1);
  22. tree[node].sum=tree[node<<1].sum+tree[node<<1|1].sum;
  23. }
  24. void pushdown(int node,int ln,int rn)
  25. {
  26. if(tree[node].lazy)
  27. {
  28. tree[node<<1].lazy+=tree[node].lazy;
  29. tree[node<<1|1].lazy+=tree[node].lazy;
  30. tree[node<<1].sum+=tree[node].lazy*ln;
  31. tree[node<<1|1].sum+=tree[node].lazy*rn;
  32. tree[node].lazy=0;
  33. }
  34. return;
  35. }
  36. void add(int L,int R,int num,int l,int r,int node)
  37. {
  38. if(L<=l&&r<=R)
  39. {
  40. tree[node].sum+=num*(r-l+1);
  41. tree[node].lazy+=num;
  42. return;
  43. }
  44. int mid=(l+r)>>1;
  45. pushdown(node,mid-l+1,r-mid);
  46. if(L<=mid) add(L,R,num,l,mid,node<<1);
  47. if(R>mid) add(L,R,num,mid+1,r,node<<1|1);
  48. tree[node].sum=tree[node<<1].sum+tree[node<<1|1].sum;
  49. }
  50. long long query(int L,int R,int l,int r,int node)
  51. {
  52. if(L<=l&&R>=r) return tree[node].sum;
  53. int mid=(l+r)>>1;
  54. pushdown(node,mid-l+1,r-mid);
  55. long long ans=0;
  56. if(L<=mid) ans+=query(L,R,l,mid,node<<1);
  57. if(R>mid) ans+=query(L,R,mid+1,r,node<<1|1);
  58. return ans;
  59. }
  60. signed main()
  61. {
  62. scanf("%lld%lld",&n,&m);
  63. for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
  64. build(1,n,1);
  65. for(int i=1;i<=m;i++)
  66. {
  67. scanf("%lld%lld%lld",&opt,&x,&y);
  68. if(opt==1) scanf("%lld",&k),add(x,y,k,1,n,1);
  69. else printf("%lld\n",query(x,y,1,n,1));
  70. }
  71. return 0;
  72. }

复杂度

*可持久化线段树
  1. #include<cstdio>
  2. #include<cstring>
  3. #define R register int
  4. const int N=1000009,M=20000009;
  5. int P,rt[N],lc[M],rc[M],val[M];
  6. char I[M<<1],O[M],*fi=I,*fo=O;
  7. bool nega;
  8. inline void in(R&z)
  9. {
  10. while(*fi<'-')++fi;
  11. if(*fi=='-')nega=1,++fi;
  12. z=*fi++&15;
  13. while(*fi>'-')z*=10,z+=*fi++&15;
  14. if(nega)nega=0,z=-z;
  15. }
  16. void oi(R z)
  17. {
  18. if(z>9)oi(z/10);
  19. *fo++=z%10|'0';
  20. }
  21. inline void out(R z)
  22. {
  23. z>0?oi(z):(*fo++='-',oi(-z));*fo++='\n';
  24. }//上面快读快写
  25. void build(R&t,R l,R r)//初始化建树,线段树基本操作
  26. {
  27. R m;
  28. t=++P;
  29. if(l!=r)
  30. {
  31. m=(l+r)>>1;
  32. build(lc[t],l,m);
  33. build(rc[t],m+1,r);
  34. }
  35. else in(val[P]);
  36. }
  37. inline void insert(R*t,R u,R l,R r,R k)//更新,插入一个新路径
  38. {
  39. R m;
  40. while(l!=r)
  41. {
  42. *t=++P;
  43. m=(l+r)>>1;
  44. if(k<=m)r=m,rc[*t]=rc[u],t=&lc[*t],u=lc[u];
  45. else l=m+1,lc[*t]=lc[u],t=&rc[*t],u=rc[u];
  46. }
  47. in(val[*t=++P]);
  48. }
  49. inline int ask(R t,R l,R r,R k)//询问
  50. {
  51. R m;
  52. while(l!=r)
  53. {
  54. m=(l+r)>>1;
  55. if(k<=m)r=m,t=lc[t];
  56. else l=m+1,t=rc[t];
  57. }
  58. return val[t];
  59. }
  60. int main()
  61. {
  62. freopen("ct.in","r",stdin);freopen("ct.out","w",stdout);
  63. fread(I,1,sizeof(I),stdin);
  64. R n,m,i,v,op,loc;
  65. in(n);in(m);
  66. build(rt[0],1,n);
  67. for(i=1;i<=m;++i)
  68. {
  69. in(v);in(op);in(loc);
  70. if(op&1)insert(&rt[i],rt[v],1,n,loc);
  71. else
  72. {
  73. out(ask(rt[v],1,n,loc));
  74. rt[i]=++P;//没错,这里的版本复制其实很简单
  75. lc[P]=lc[rt[v]];
  76. rc[P]=rc[rt[v]];
  77. }
  78. }
  79. fwrite(O,1,fo-O,stdout);
  80. fclose(stdin);fclose(stdout);
  81. return 0;
  82. }
*权值线段树
  1. //problemID:https://www.luogu.org/problemnew/show/P1801
  2. #include<bits/stdc++.h>
  3. #define N 200005
  4. using namespace std;
  5. int m,n,k;
  6. int a[N],b[N],u[N];
  7. struct MM{
  8. int l,r,s;
  9. }tree[N<<2];
  10. inline void build(int root,int L,int R)
  11. {
  12. tree[root].l=L;
  13. tree[root].r=R;
  14. if(L==R) return;
  15. int mid=(L+R)>>1;
  16. build(root<<1,L,mid);
  17. build(root<<1|1,mid+1,R);
  18. }
  19. inline void update(int root,int t)
  20. {
  21. if(tree[root].l==tree[root].r)
  22. {
  23. tree[root].s++;//个数加一
  24. return;
  25. }
  26. int mid=(tree[root].l+tree[root].r)>>1;
  27. if(t<=mid) update(root<<1,t);
  28. else update(root<<1|1,t);
  29. tree[root].s=tree[root<<1].s+tree[root<<1|1].s;
  30. }
  31. inline int query(int root,int t)
  32. {
  33. if(tree[root].l==tree[root].r)
  34. return tree[root].l;
  35. if(t<=tree[root<<1].s) return query(root<<1,t);
  36. else return query(root<<1|1,t-tree[root<<1].s);
  37. }
  38. int main()
  39. {
  40. cin>>m>>n;
  41. for(int i=1;i<=m;i++)
  42. {
  43. cin>>a[i];
  44. b[i]=a[i];
  45. }
  46. for(int i=1;i<=n;i++)
  47. cin>>u[i];
  48. sort(b+1,b+m+1);
  49. int s=unique(b+1,b+m+1)-(b+1);//离散化(如果值域很大的话),s是数组b中不重复的数的个数
  50. build(1,1,s);//依s建树
  51. int h=0;
  52. while(n!=h)
  53. {
  54. h++;
  55. for(int i=u[h-1]+1;i<=u[h];i++)
  56. {
  57. int v=lower_bound(b+1,b+s+1,a[i])-b;//v是a[i]在数组b中所处的位置(注意之前数组b排了序)
  58. update(1,v);
  59. }
  60. cout<<b[query(1,++k)]<<endl;
  61. }
  62. return 0;
  63. }
*动态开点线段树
  1. //problemID:http://acm.hdu.edu.cn/showproblem.php?pid=6183
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstdio>
  5. #include<cstring>
  6. #define clr(a,b) memset(a,b,sizeof a)
  7. #define rep(i,j,k) for(int i=j;i<=k;i++)
  8. using namespace std;
  9. const int maxn = 2e6+5e5+11;
  10. const int oo = 0x3f3f3f3f;
  11. int T[70];
  12. struct ST{
  13. int L[maxn<<2],R[maxn<<2],minx[maxn<<2];
  14. int tot;
  15. void init(){
  16. clr(T,0);
  17. tot=0;
  18. L[0]=R[0]=0;
  19. minx[0]=oo;
  20. }
  21. void pu(int o){
  22. minx[o]=min(minx[L[o]],minx[R[o]]);
  23. }
  24. void update(int &o,int l,int r,int y,int x){
  25. if(!o){
  26. o=++tot;
  27. L[o]=R[o]=0;
  28. minx[o]=x;
  29. }
  30. if(l==r){
  31. minx[o]=min(minx[o],x);
  32. return ;
  33. }
  34. int m=l+r>>1;
  35. if(y<=m) update(L[o],l,m,y,x);
  36. else update(R[o],m+1,r,y,x);
  37. pu(o);
  38. }
  39. int query(int &o,int l,int r,int LL,int RR){
  40. if(!o){
  41. return oo; //很重要!!!
  42. }
  43. if(LL<=l&&r<=RR){
  44. return minx[o];
  45. }
  46. int m=l+r>>1;
  47. int ans=oo;
  48. if(LL<=m) ans=min(ans,query(L[o],l,m,LL,RR));
  49. if(RR>m) ans=min(ans,query(R[o],m+1,r,LL,RR));
  50. return ans;
  51. }
  52. }st;
  53. const int n = 1e6;
  54. int main(){
  55. int op,x,y,yy1,yy2,c;
  56. st.init();
  57. while(scanf("%d",&op)!=EOF){
  58. if(op==3)break;
  59. else if(op==0) st.init();
  60. else if(op==1){
  61. scanf("%d%d%d",&x,&y,&c);
  62. st.update(T[c],1,n,y,x);
  63. }
  64. else if(op==2){
  65. scanf("%d%d%d",&x,&yy1,&yy2);
  66. int ans=0,tmp;
  67. rep(i,0,50){
  68. tmp=st.query(T[i],1,n,yy1,yy2);
  69. if(tmp<=x) ans++;
  70. }
  71. printf("%d\n",ans);
  72. }
  73. }
  74. return 0;
  75. }

树状数组

【简介】

树状数组(Binary Indexed Tree, Fenwick Tree)是一个查询和修改复杂度都为的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3374
  2. #include <iostream>
  3. #include <cstdio>
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <cstring>
  7. using namespace std;
  8. int n,m,tree[2000010];
  9. int lowbit(int k)
  10. {
  11. return k & -k;
  12. }
  13. void add(int x,int k)
  14. {
  15. while(x<=n)
  16. {
  17. tree[x]+=k;
  18. x+=lowbit(x);
  19. }
  20. }
  21. int sum(int x)
  22. {
  23. int ans=0;
  24. while(x!=0)
  25. {
  26. ans+=tree[x];
  27. x-=lowbit(x);
  28. }
  29. return ans;
  30. }
  31. int main()
  32. {
  33. cin>>n>>m;
  34. for(int i=1;i<=n;i++)
  35. {
  36. int a;
  37. scanf("%d",&a);
  38. add(i,a);
  39. }
  40. for(int i=1;i<=m;i++)
  41. {
  42. int a,b,c;
  43. scanf("%d%d%d",&a,&b,&c);
  44. if(a==1)
  45. add(b,c);
  46. if(a==2)
  47. cout<<sum(c)-sum(b-1)<<endl;
  48. }
  49. return 0;
  50. }

复杂度

ST表

【简介】

ST表类似树状数组,线段树这两种数据结构,是一种用于解决RMQ(Range Minimum/Maximum Query,即区间最值查询)问题的离线数据结构。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3865
  5. #include<cstdio>
  6. #include<cmath>
  7. #define max(x,y) (x)>(y)?(x):(y)
  8. #define N 100001
  9. int n,q,a[N],ST[N][20],l,r,p;
  10. int main()
  11. {
  12. scanf("%d%d",&n,&q);
  13. for(int i=1;i<=n;i++) scanf("%d",&a[i]),ST[i][0]=a[i];
  14. for(int k=1;k<=20;k++)
  15. for(int i=1;i<=n;i++)
  16. if(i+(1<<k)-1<=n) ST[i][k]=max(ST[i][k-1],ST[i+(1<<(k-1))][k-1]);
  17. for(int i=1;i<=q;i++)
  18. {
  19. scanf("%d%d",&l,&r);
  20. p=log2(r-l+1);
  21. printf("%d\n",max(ST[l][p],ST[r-(1<<p)+1][p]));
  22. }
  23. return 0;
  24. }

复杂度

*块状链表

【简介】

其基本定应用为:把一个长度为的串,分成约块,相邻两块的大小不小于根号,每一块的大小不超过。这样就可以在的时间内解决一个插入、询问、拆分、合并等等的操作。

【代码实现】

  1. //problemID:https://www.lydsy.com/JudgeOnline/problem.php?id=3337
  2. #include<cstring>
  3. #include<iostream>
  4. #include<cctype>
  5. #include<cstdio>
  6. #include<algorithm>
  7. #include<queue>
  8. #include<cmath>
  9. #define writ(x,c) write(x),putchar(c);
  10. typedef long long ll;
  11. const int N=10000,MAX=1<<29;
  12. using namespace std;
  13. inline int read()
  14. {
  15. char c;int x=0;bool f=0;
  16. for(;!isdigit(c);c=getchar()) if(c=='-') f=1;
  17. for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
  18. return (f ? -x : x);
  19. }
  20. template <class _T>
  21. void write(_T x)
  22. {
  23. if(x<0) putchar('-'),x=-x;
  24. if(x>9) write(x/10);
  25. putchar(x%10+'0');
  26. }
  27. int n,m,size;
  28. struct node
  29. {
  30. int d[1003],s[1003],rev,add,same,size,next;
  31. ll sum;
  32. }a[N];
  33. queue<int> q;
  34. void Init()
  35. {
  36. }
  37. int new_node()
  38. {
  39. int temp=q.front();q.pop();
  40. return temp;
  41. }
  42. void clear(int x){a[x].rev=a[x].add=a[x].same=a[x].size=a[x].rev=0;}
  43. void erase(int x){q.push(x);clear(x);}
  44. void find(int &pos,int &now)
  45. {
  46. for(now=0;a[now].next!=-1&&pos>a[now].size;now=a[now].next)
  47. pos-=a[now].size;
  48. }
  49. void down(int now)
  50. {
  51. int tot=a[now].size;
  52. if(a[now].rev)
  53. {
  54. a[now].rev=false;
  55. int tt=(tot>>1);
  56. for(int u=1;u<=tt;u++) swap(a[now].d[u],a[now].d[tot-u+1]);
  57. }
  58. if(a[now].same!=0)
  59. {
  60. for(int u=1;u<=tot;u++)
  61. a[now].d[u]=a[now].same;
  62. a[now].sum=a[now].same*tot,a[now].same=0;
  63. }
  64. if(a[now].add!=0)
  65. {
  66. for(int u=1;u<=tot;u++)
  67. a[now].d[u]+=a[now].add;
  68. a[now].sum=a[now].sum+a[now].add*tot,a[now].add=0;
  69. }
  70. }
  71. void update(int x)
  72. {
  73. a[x].sum=0;
  74. for(int u=1;u<=a[x].size;u++)
  75. a[x].sum+=a[x].d[u],a[x].s[u]=a[x].d[u];
  76. sort(a[x].s+1,a[x].s+a[x].size+1);
  77. }
  78. void spilt(int now,int pos)
  79. {
  80. down(now);
  81. int t=new_node();
  82. for(int u=pos;u<=a[now].size;u++)
  83. a[t].d[++a[t].size]=a[now].d[u];
  84. a[t].next=a[now].next,a[now].next=t,a[now].size=max(pos-1,0);
  85. update(t);update(now);
  86. }
  87. void Merge(int now)
  88. {
  89. int k=a[now].next;
  90. down(now);down(k);
  91. for(int u=1;u<=a[k].size;u++)
  92. a[now].d[++a[now].size]=a[k].d[u];
  93. a[now].next=a[k].next;erase(k);
  94. update(now);
  95. }
  96. void maintain(int now)
  97. {
  98. for(;now!=-1;now=a[now].next)
  99. if(a[now].next!=-1&&a[now].size+a[a[now].next].size<=size)
  100. Merge(now);
  101. }
  102. void ins(int pos,int x)
  103. {
  104. int now;pos++;
  105. find(pos,now);spilt(now,pos);
  106. a[now].d[++a[now].size]=x,a[now].sum+=x;
  107. int lalal;
  108. for(lalal=1;lalal<a[now].size;lalal++)
  109. if(a[now].s[lalal]>x) break;
  110. for(int u=a[now].size;u>lalal;u--)
  111. a[now].s[u]=a[now].s[u-1];
  112. a[now].s[lalal]=x;
  113. maintain(now);
  114. }
  115. void del(int pos)
  116. {
  117. int now;
  118. find(pos,now);down(now);
  119. for(int u=pos+1;u<=a[now].size;u++)
  120. a[now].d[u-1]=a[now].d[u];
  121. a[now].size--;
  122. update(now);maintain(now);
  123. }
  124. void solve(int l,int r,int &lp,int &rp)
  125. {
  126. int pos=l;
  127. find(pos,lp);spilt(lp,pos);
  128. pos=r+1;
  129. find(pos,rp);spilt(rp,pos);
  130. pos=r;
  131. find(pos,rp);
  132. }
  133. int st[N];
  134. void rverse(int l,int r)
  135. {
  136. int lp,rp;
  137. solve(l,r,lp,rp);
  138. int now=lp,top=0;
  139. for(int u=a[lp].next;u!=a[rp].next;u=a[u].next)
  140. st[++top]=u,a[u].rev^=1;
  141. a[st[1]].next=a[rp].next;
  142. for(int u=top;u>1;u--)
  143. a[st[u]].next=st[u-1];
  144. a[lp].next=rp;
  145. maintain(lp);
  146. }
  147. void slip(int l,int r,int k)
  148. {
  149. int lp,mp,rp,np;
  150. solve(l,r-k,lp,mp),solve(r-k+1,r,mp,rp);
  151. np=a[lp].next,a[lp].next=a[mp].next,a[mp].next=a[rp].next,a[rp].next=np;
  152. maintain(lp);
  153. }
  154. void add(int l,int r,int val)
  155. {
  156. int lp,rp;
  157. solve(l,r,lp,rp);
  158. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  159. a[now].add+=val,a[now].sum=a[now].sum+a[now].size*val;
  160. maintain(lp);
  161. }
  162. void same(int l,int r,int val)
  163. {
  164. int lp,rp;
  165. solve(l,r,lp,rp);
  166. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  167. a[now].add=0,a[now].same=val,a[now].sum=a[now].size*val;
  168. maintain(lp);
  169. }
  170. ll getsum(int l,int r)
  171. {
  172. int lp,rp;
  173. solve(l,r,lp,rp);
  174. ll ans=0;
  175. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  176. ans=ans+a[now].sum;
  177. maintain(lp);
  178. return ans;
  179. }
  180. int getcha(int l,int r)
  181. {
  182. int lp,rp;
  183. solve(l,r,lp,rp);
  184. int maxx=-MAX,minn=MAX;
  185. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  186. if(a[now].size!=0)
  187. if(a[now].same!=0)
  188. minn=min(minn,a[now].same+a[now].add),
  189. maxx=max(maxx,a[now].same+a[now].add);
  190. else
  191. minn=min(minn,a[now].s[1]+a[now].add),
  192. maxx=max(maxx,a[now].s[a[now].size]+a[now].add);
  193. maintain(lp);
  194. return maxx-minn;
  195. }
  196. int near(int l,int r,int val)
  197. {
  198. int lp,rp;
  199. solve(l,r,lp,rp);
  200. int ans=MAX;
  201. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  202. {
  203. if(a[now].same)
  204. ans=min(ans,abs(val-a[now].same-a[now].add));
  205. else
  206. {
  207. int id=lower_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add)-a[now].s;
  208. if(id!=a[now].size+1)
  209. ans=min(ans,a[now].s[id]+a[now].add-val);
  210. if(id!=1)
  211. id--,ans=min(ans,val-a[now].s[id]-a[now].add);
  212. }
  213. }
  214. maintain(lp);
  215. return ans;
  216. }
  217. int rank(int l,int r,int k)
  218. {
  219. int lp,rp;
  220. solve(l,r,lp,rp);
  221. int ll=0,rr=MAX;
  222. while(ll<rr)
  223. {
  224. int mid=(ll+rr)/2+1,sum=1;
  225. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  226. {
  227. if(a[now].same!=0)
  228. {
  229. if(a[now].same+a[now].add<mid)
  230. sum=sum+a[now].size;
  231. }
  232. else
  233. {
  234. int id=upper_bound(a[now].s+1,a[now].s+a[now].size+1,mid-a[now].add-1)-a[now].s;
  235. sum=sum+max(0,id-1);
  236. }
  237. }
  238. if(k>=sum) ll=mid;
  239. else rr=mid-1;
  240. }
  241. maintain(lp);
  242. return ll;
  243. }
  244. int sec(int l,int r,int val)
  245. {
  246. int lp,rp;
  247. solve(l,r,lp,rp);
  248. int ans=0;
  249. for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
  250. {
  251. if(a[now].same!=0)
  252. {
  253. if(a[now].same+a[now].add<val)
  254. ans=ans+a[now].size;
  255. }
  256. else
  257. {
  258. int it=upper_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add-1)-a[now].s;
  259. ans=ans+it-1;
  260. }
  261. }
  262. maintain(lp);
  263. return ans;
  264. }
  265. int main()
  266. {
  267. n=read(),size=sqrt(n);
  268. for(int i=1;i<N;i++) q.push(i);a[0].next=-1;a[0].size=0;
  269. for(int u=1;u<=n;u++)
  270. {
  271. int x=read();ins(u-1,x);
  272. }
  273. m=read();
  274. while(m--)
  275. {
  276. register int op,x,y,z;op=read();
  277. switch(op)
  278. {
  279. case 1:x=read();y=read();ins(x,y);break;
  280. case 2:x=read();del(x);break;
  281. case 3:x=read();y=read();rverse(x,y);break;
  282. case 4:x=read();y=read();z=read();slip(x,y,z);break;
  283. case 5:x=read();y=read();z=read();add(x,y,z);break;
  284. case 6:x=read();y=read();z=read();same(x,y,z);break;
  285. case 7:x=read();y=read();writ(getsum(x,y),'\n');break;
  286. case 8:x=read();y=read();writ(getcha(x,y),'\n');break;
  287. case 9:x=read();y=read();z=read();writ(near(x,y,z),'\n');break;
  288. case 10:x=read();y=read();z=read();writ(rank(x,y,z),'\n');break;
  289. case 11:x=read();y=read();z=read();writ(sec(x,y,z),'\n');break;
  290. }
  291. }
  292. return 0;
  293. }

复杂度

*Splay(伸展树)

【简介】

伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在内完成插入、查找和删除操作。它由Daniel Sleator和Robert Tarjan创造,后勃刚对其进行了改进。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3391
  2. #include<bits/stdc++.h>
  3. #define N 100005
  4. using namespace std;
  5. int n,m;
  6. int fa[N],ch[N][2],size[N],rev[N],rt;
  7. inline void pushup(int x){
  8. size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
  9. }
  10. void pushdown(int x){
  11. if(rev[x]){
  12. swap(ch[x][0],ch[x][1]);
  13. rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;rev[x]=0;
  14. }
  15. }
  16. void rotate(int x,int &k){
  17. int y=fa[x],z=fa[y],kind;
  18. if(ch[y][0]==x)kind=1;else kind=0;
  19. if(y==k)k=x;
  20. else{if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
  21. ch[y][kind^1]=ch[x][kind];fa[ch[y][kind^1]]=y;
  22. ch[x][kind]=y;fa[y]=x;fa[x]=z;
  23. pushup(x);pushup(y);
  24. }
  25. void splay(int x,int &k){
  26. while(x!=k){
  27. int y=fa[x],z=fa[y];
  28. if(y!=k){
  29. if((ch[y][0]==x)^(ch[z][0]==y))rotate(x,k);
  30. else rotate(y,k);
  31. }
  32. rotate(x,k);
  33. }
  34. }
  35. void build(int l,int r,int f){
  36. if(l>r)return;
  37. int mid=(l+r)/2;
  38. if(mid<f)ch[f][0]=mid;else ch[f][1]=mid;
  39. fa[mid]=f;size[mid]=1;
  40. if(l==r)return;
  41. build(l,mid-1,mid);build(mid+1,r,mid);
  42. pushup(mid);
  43. }
  44. int find(int x,int k){
  45. pushdown(x);int s=size[ch[x][0]];
  46. if(k==s+1)return x;
  47. if(k<=s)return find(ch[x][0],k);
  48. else return find(ch[x][1],k-s-1);
  49. }
  50. void rever(int l,int r){
  51. int x=find(rt,l),y=find(rt,r+2);
  52. splay(x,rt);splay(y,ch[x][1]);int z=ch[y][0];
  53. rev[z]^=1;
  54. }
  55. int main(){
  56. scanf("%d%d",&n,&m);
  57. rt=(n+3)/2;build(1,n+2,rt);
  58. for(int i=1;i<=m;i++){
  59. int L,R;scanf("%d%d",&L,&R);
  60. rever(L,R);
  61. }
  62. for(int i=2;i<=n+1;i++)printf("%d ",find(rt,i)-1);
  63. return 0;
  64. }

复杂度

*Treap

【简介】

树堆,在数据结构中也称Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为
相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构。

【代码实现】

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include<climits>
  7. typedef long long LL;
  8. using namespace std;
  9. int RD()
  10. {
  11. int out = 0,flag = 1;char c = getchar();
  12. while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
  13. while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
  14. return flag * out;
  15. }
  16. //第一次打treap,不压行写注释XD
  17. const int maxn = 1000019,INF = 1e9;
  18. //平衡树,利用BST性质查询和修改,利用随机和堆优先级来保持平衡,把树的深度控制在log N,保证了操作效率
  19. //基本平衡树有以下几个比较重要的函数:新建,插入,删除,旋转
  20. //节点的基本属性有val(值),dat(随机出来的优先级)
  21. //通过增加属性,结合BST的性质可以达到一些效果,如size(子树大小,查询排名),cnt(每个节点包含的副本数)等
  22. int na;
  23. int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子
  24. int val[maxn],dat[maxn];
  25. int size[maxn],cnt[maxn];
  26. int tot,root;
  27. int New(int v)
  28. {//新增节点,
  29. val[++tot] = v;//节点赋值
  30. dat[tot] = rand();//随机优先级
  31. size[tot] = 1;//目前是新建叶子节点,所以子树大小为1
  32. cnt[tot] = 1;//新建节点同理副本数为1
  33. return tot;
  34. }
  35. void pushup(int id)
  36. {//和线段树的pushup更新一样
  37. size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];//本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数
  38. }
  39. void build()
  40. {
  41. root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)
  42. pushup(root);//因为INF > -INF,所以是右子树,
  43. }
  44. void Rotate(int &id,int d)
  45. {//id是引用传递,d(irection)为旋转方向,0为左旋,1为右旋
  46. int temp = ch[id][d ^ 1];//旋转理解:找个动图看一看就好(或参见其他OIer的blog)
  47. ch[id][d ^ 1] = ch[temp][d];//这里讲一个记忆技巧,这些数据都是被记录后马上修改
  48. ch[temp][d] = id;//所以像“Z”一样
  49. id = temp;//比如这个id,在上一行才被记录过,ch[temp][d]、ch[id][d ^ 1]也是一样的
  50. pushup(ch[id][d]),pushup(id);//旋转以后size会改变,看图就会发现只更新自己和转上来的点,pushup一下,注意先子节点再父节点
  51. }//旋转实质是({在满足BST的性质的基础上比较优先级}通过交换本节点和其某个叶子节点)把链叉开成二叉形状(从而控制深度),可以看图理解一下
  52. void insert(int &id,int v)
  53. {//id依然是引用,在新建节点时可以体现
  54. if(!id){
  55. id = New(v);//若节点为空,则新建一个节点
  56. return ;
  57. }
  58. if(v == val[id])cnt[id]++;//若节点已存在,则副本数++;
  59. else{//要满足BST性质,小于插到左边,大于插到右边
  60. int d = v < val[id] ? 0 : 1;//这个d是方向的意思,按照BST的性质,小于本节点则向左,大于向右
  61. insert(ch[id][d],v);//递归实现
  62. if(dat[id] < dat[ch[id][d]])Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋
  63. }
  64. pushup(id);//现在更新一下本节点的信息
  65. }
  66. void Remove(int &id,int v)
  67. {//最难de部分了
  68. if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回
  69. if(v == val[id]){//检索到了这个值
  70. if(cnt[id] > 1){cnt[id]--,pushup(id);return ;}//若副本不止一个,减去一个就好
  71. if(ch[id][0] || ch[id][1]){//发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
  72. if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]){//当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来
  73. Rotate(id,1),Remove(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点
  74. }
  75. else Rotate(id,0),Remove(ch[id][0],v);
  76. pushup(id);
  77. }
  78. else id = 0;//发现本节点是叶子节点,直接删除
  79. return ;//这个return对应的是检索到值de所有情况
  80. }
  81. v < val[id] ? Remove(ch[id][0],v) : Remove(ch[id][1],v);//继续BST性质
  82. pushup(id);
  83. }
  84. int get_rank(int id,int v)
  85. {
  86. if(!id)return 0;//若查询值不存在,返回
  87. if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1
  88. else if(v < val[id])return get_rank(ch[id][0],v);//发现需查询的点在该点左边,往左边递归查询
  89. else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);//若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]
  90. }
  91. int get_val(int id,int rank)
  92. {
  93. if(!id)return INF;//一直向右找找不到,说明是正无穷
  94. if(rank <= size[ch[id][0]])return get_val(ch[id][0],rank);//左边排名已经大于rank了,说明rank对应的值在左儿子那里
  95. else if(rank <= size[ch[id][0]] + cnt[id])return val[id];//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,则直接返回目前节点(中区间)的值
  96. else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);//剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归
  97. }
  98. int get_pre(int v)
  99. {
  100. int id = root,pre;//递归不好返回,以循环求解
  101. while(id){//查到节点不存在为止
  102. if(val[id] < v)pre = val[id],id = ch[id][1];//满足当前节点比目标小,往当前节点的右侧寻找最优值
  103. else id = ch[id][0];//无论是比目标节点大还是等于目标节点,都不满足前驱条件,应往更小处靠近
  104. }
  105. return pre;
  106. }
  107. int get_next(int v)
  108. {
  109. int id = root,next;
  110. while(id){
  111. if(val[id] > v)next = val[id],id = ch[id][0];//同理,满足条件向左寻找更小解(也就是最优解)
  112. else id = ch[id][1];//与上方同理
  113. }
  114. return next;
  115. }
  116. int main()
  117. {
  118. build();//不要忘记初始化[运行build()会连同root一并初始化,所以很重要]
  119. na = RD();
  120. for(int i = 1;i <= na;i++){
  121. int cmd = RD(),x = RD();
  122. if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询
  123. else if(cmd == 2)Remove(root,x);
  124. else if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
  125. else if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INF
  126. else if(cmd == 5)printf("%d\n",get_pre(x));
  127. else if(cmd == 6)printf("%d\n",get_next(x));
  128. }
  129. return 0;
  130. }

复杂度

*范浩强treap

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3369
  2. #include<iostream>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<cstring>
  6. #include<algorithm>
  7. using namespace std;
  8. struct node{
  9. int siz,val,key,lch,rch;
  10. }t[100005];
  11. int tot,seed=233,root=1;
  12. int Rand(){//随机key值
  13. return seed=int(seed*482711ll%2147483647);
  14. }
  15. int NEW(int val){//新建节点
  16. t[++tot].siz=1;
  17. t[tot].val=val;
  18. t[tot].key=Rand();
  19. t[tot].lch=t[tot].rch=0;
  20. return tot;
  21. }
  22. void update(int now){//维护子树大小
  23. t[now].siz=t[t[now].lch].siz+t[t[now].rch].siz+1;
  24. }
  25. void split(int now,int &a,int &b,int val){//拆分操作
  26. //now原Treap,a左树,b右树,val判定值
  27. //注意传地址符
  28. if(now==0){
  29. a=b=0;//若now=0分割完毕;
  30. return;
  31. }
  32. if(t[now].val<=val)//因为now左子树中的所有值都小于now的值,所以若now属于左树,那么他们都属于左树递归now的右子树;
  33. a=now,split(t[now].rch,t[a].rch,b,val);//a=now已经使a的右子树=now的右子树,不再递归a的右子树;
  34. else//同上now右子树也都属于左树,递归左子树;
  35. b=now,split(t[now].lch,a,t[b].lch,val);
  36. update(now);//因为后面会用到左(右)树的siz所以更新维护
  37. }
  38. void merge(int &now,int a,int b){//合并操作
  39. //now新树
  40. if(a==0||b==0){
  41. now=a+b;//若某个树已空,则将另一个树整体插入
  42. return;
  43. }
  44. //按照key值合并(堆性质)
  45. if(t[a].key<t[b].key)//若a树key值<b树,那么b树属于a树的后代,因为b树恒大于a树,那么b树一定属于a树的右后代,a的左子树不变,直接赋给now,递归合并a的右子树和b
  46. now=a,merge(t[now].rch,t[a].rch,b);
  47. else
  48. now=b,merge(t[now].lch,a,t[b].lch);//同理,a树一定是b树的左儿子,递归合并b的左儿子和a
  49. update(now);
  50. }
  51. void find(int now,int rank){//找第k大
  52. while(t[t[now].lch].siz+1!=rank){
  53. if(t[t[now].lch].siz>=rank)
  54. now=t[now].lch;//若左子树大小大于rank,找左子树
  55. else
  56. rank-=(t[t[now].lch].siz+1),now=t[now].rch;//找右子树(rank-左子树大小-树根(大小为1))号的元素
  57. }
  58. printf("%d\n",t[now].val);
  59. }
  60. void insert(int val){
  61. int x=0,y=0,z;
  62. z=NEW(val);//新建节点z,作为z树
  63. split(root,x,y,val);//将树分为两部分,x树为<=待插入值,y树大于
  64. merge(x,x,z);//合并x树和新节点z(树),赋给x树
  65. merge(root,x,y);//合并新x树和y树,赋给根
  66. //就这么简单
  67. }
  68. void delet(int val){
  69. int x=0,y=0,z=0;
  70. split(root,x,y,val);//分为x树为<=待删除,y树大于
  71. split(x,x,z,val-1);//x树分为新x树<待删除,z树等于待删除
  72. merge(z,t[z].lch,t[z].rch);//合并z树左右儿子,赋给z树,即丢弃z树根节点(实现删除)
  73. merge(x,x,z);
  74. merge(root,x,y);//合并,不在重复
  75. }
  76. void get_rank(int val){
  77. int x=0,y=0;
  78. split(root,x,y,val-1);//分为小于待查找值的x树和大于等于的y树
  79. printf("%d\n",t[x].siz+1);//即为待查找值的编号
  80. merge(root,x,y);//合并
  81. }
  82. void get_val(int rank){
  83. find(root,rank);//find查找即可
  84. }
  85. void get_pre(int val){
  86. int x=0,y=0;
  87. split(root,x,y,val-1);//x树为<=val-1值即小于val值
  88. find(x,t[x].siz);//在小于val值中找最大的(编号为siz的)就是前驱
  89. merge(root,x,y);
  90. }
  91. void get_nxt(int val){
  92. int x=0,y=0;
  93. split(root,x,y,val);//x树小于等于val值,那么y树大于val值
  94. find(y,1);//在y树中找最小的,即为后继
  95. merge(root,x,y);//合并
  96. }
  97. int main(){
  98. int i,j,k,m;
  99. NEW(2147483627);//初始虚节点
  100. t[1].siz=0;//siz为0,不算虚节点的大小
  101. scanf("%d",&m);
  102. for(i=1;i<=m;i++){
  103. scanf("%d%d",&j,&k);
  104. if(j==1)insert(k);
  105. if(j==2)delet(k);
  106. if(j==3)get_rank(k);
  107. if(j==4)get_val(k);
  108. if(j==5)get_pre(k);
  109. if(j==6)get_nxt(k);
  110. }
  111. return 0;
  112. }

复杂度

*替罪羊树

【简介】

替罪羊树是计算机科学中,一种基于部分重建的自平衡二叉搜索树。在替罪羊树上,插入或删除节点的平摊最坏时间复杂度是,搜索节点的最坏时间复杂度是

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3369
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <iostream>
  5. #include <algorithm>
  6. #define alpha 0.8
  7. #define maxn 2000001
  8. #define ri register
  9. #define il inline
  10. using namespace std;
  11. struct scapegoat{
  12. int son[2], val, valid, total;
  13. bool exist;
  14. }e[maxn];
  15. int memory[maxn];
  16. int cur[maxn];
  17. int root, pool, poi, cnt, to_rebuild;
  18. il bool isbad(int now)
  19. {
  20. if((double)e[now].valid*alpha <= (double)max(e[e[now].son[0]].valid, e[e[now].son[1]].valid)) return true;
  21. return false;
  22. }
  23. void dfs(int now)
  24. {
  25. if(!now) return;
  26. dfs(e[now].son[0]);
  27. if(e[now].exist) cur[++poi] = now;
  28. else memory[++pool] = now;
  29. dfs(e[now].son[1]);
  30. }
  31. void build(int l, int r, int &now)
  32. {
  33. int mid = l+r>>1;
  34. now = cur[mid];
  35. if(l == r)
  36. {
  37. e[now].son[0] = e[now].son[1] = 0;
  38. e[now].total = e[now].valid = 1;
  39. return;
  40. }
  41. if(l < mid) build(l,mid-1,e[now].son[0]);
  42. else e[now].son[0] = 0;
  43. build(mid+1,r,e[now].son[1]);
  44. e[now].total = e[e[now].son[0]].total + e[e[now].son[1]].total + 1;
  45. e[now].valid = e[e[now].son[0]].valid + e[e[now].son[1]].valid + 1;
  46. }
  47. il void rebuild(int &now)
  48. {
  49. poi = 0;
  50. dfs(now);
  51. if(poi) build(1,poi,now);
  52. else now = 0;
  53. }
  54. il int find_rank(int k)
  55. {
  56. int now = root;
  57. int ans = 1;
  58. while(now)
  59. {
  60. if(e[now].val >= k) now = e[now].son[0];
  61. else
  62. {
  63. ans += e[e[now].son[0]].valid + e[now].exist;
  64. now = e[now].son[1];
  65. }
  66. }
  67. return ans;
  68. }
  69. il int find_kth(int k)
  70. {
  71. int now = root;
  72. while(now)
  73. {
  74. if(e[now].exist&&e[e[now].son[0]].valid+1 == k) return e[now].val;
  75. else if(e[e[now].son[0]].valid >= k) now = e[now].son[0];
  76. else
  77. {
  78. k -= e[e[now].son[0]].valid + e[now].exist;
  79. now = e[now].son[1];
  80. }
  81. }
  82. }
  83. void insert(int &now, int val)
  84. {
  85. if(!now)
  86. {
  87. now = memory[pool--]; e[now].val = val;
  88. e[now].exist = e[now].total = e[now].valid = 1;
  89. e[now].son[0] = e[now].son[1] = 0;
  90. return;
  91. }
  92. e[now].total++, e[now].valid++;
  93. if(e[now].val >= val) insert(e[now].son[0], val);
  94. else insert(e[now].son[1], val);
  95. if(!isbad(now))
  96. {
  97. if(to_rebuild)
  98. {
  99. if(e[now].son[0] == to_rebuild) rebuild(e[now].son[0]);
  100. else rebuild(e[now].son[1]);
  101. to_rebuild = 0;
  102. }
  103. }
  104. else to_rebuild = now;
  105. }
  106. il void delete_pos(int &now, int tar) //target(目标)
  107. {
  108. if(e[now].exist&&e[e[now].son[0]].valid+ 1 == tar)
  109. {
  110. e[now].exist = 0; e[now].valid--; return;
  111. }
  112. e[now].valid--;
  113. if(e[e[now].son[0]].valid + e[now].exist >= tar) delete_pos(e[now].son[0], tar);
  114. else delete_pos(e[now].son[1],tar-e[e[now].son[0]].valid-e[now].exist);
  115. }
  116. il void delete_val(int tar)
  117. {
  118. delete_pos(root, find_rank(tar));
  119. if((double)e[root].total*alpha > e[root].valid) rebuild(root);
  120. }
  121. int main()
  122. {
  123. int opt, x, m;
  124. for(int i = 2000000; i >= 1; i--) memory[++pool] = i;
  125. scanf("%d",&m);
  126. while(m--)
  127. {
  128. scanf("%d%d",&opt,&x);
  129. if(opt == 1) {insert(root, x);}
  130. if(opt == 2) {delete_val(x);}
  131. if(opt == 3) {printf("%d\n",find_rank(x));}
  132. if(opt == 4) {printf("%d\n",find_kth(x));}
  133. if(opt == 5) {printf("%d\n",find_kth(find_rank(x)-1));}
  134. if(opt == 6) {printf("%d\n",find_kth(find_rank(x+1)));}
  135. }
  136. return 0;
  137. }

复杂度

*树套树

【代码实现】

  1. #include <algorithm>
  2. #include <iostream>
  3. #include <fstream>
  4. #include <cstring>
  5. #include <cstdlib>
  6. #include <complex>
  7. #include <string>
  8. #include <cstdio>
  9. #include <vector>
  10. #include <bitset>
  11. #include <cmath>
  12. #include <ctime>
  13. #include <queue>
  14. #include <stack>
  15. #include <map>
  16. #include <set>
  17. using namespace std;
  18. const int MAXN = 1e5 + 100;
  19. int n, m;
  20. int a[MAXN];
  21. namespace Treap
  22. {
  23. struct balanced
  24. {
  25. int w;
  26. int sz;
  27. int num;
  28. int fix;
  29. int ch[2];
  30. };
  31. int tot;
  32. balanced tree[MAXN * 20];
  33. int newnode(int w)
  34. {
  35. ++tot;
  36. tree[tot].w = w;
  37. tree[tot].fix = rand();
  38. tree[tot].num = 1;
  39. tree[tot].ch[0] = tree[tot].ch[1] = 0;
  40. tree[tot].sz = 1;
  41. return tot;
  42. }
  43. void pushup(int p)
  44. {
  45. tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
  46. }
  47. void rotate(int &p, int d)
  48. {
  49. int y = tree[p].ch[d];
  50. tree[p].ch[d] = tree[y].ch[d ^ 1];
  51. tree[y].ch[d ^ 1] = p;
  52. pushup(p);
  53. pushup(y);
  54. p = y;
  55. }
  56. void insert(int &p, int w)
  57. {
  58. if (!p)
  59. p = newnode(w);
  60. else if (tree[p].w == w)
  61. ++tree[p].num;
  62. else
  63. {
  64. if (tree[p].w > w)
  65. {
  66. insert(tree[p].ch[0], w);
  67. if (tree[tree[p].ch[0]].fix > tree[p].fix)
  68. rotate(p, 0);
  69. }
  70. else
  71. {
  72. insert(tree[p].ch[1], w);
  73. if (tree[tree[p].ch[1]].fix > tree[p].fix)
  74. rotate(p, 1);
  75. }
  76. }
  77. pushup(p);
  78. }
  79. void remove(int &p, int w)
  80. {
  81. if (tree[p].w > w)
  82. remove(tree[p].ch[0], w);
  83. else if (tree[p].w < w)
  84. remove(tree[p].ch[1], w);
  85. else
  86. {
  87. if (tree[p].num > 1)
  88. --tree[p].num;
  89. else
  90. {
  91. if (!tree[p].ch[0] && !tree[p].ch[1])
  92. p = 0;
  93. else if (!tree[p].ch[0])
  94. {
  95. rotate(p, 1);
  96. remove(tree[p].ch[0], w);
  97. }
  98. else if (!tree[p].ch[1])
  99. {
  100. rotate(p, 0);
  101. remove(tree[p].ch[1], w);
  102. }
  103. else
  104. {
  105. if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix)
  106. {
  107. rotate(p, 0);
  108. remove(tree[p].ch[1], w);
  109. }
  110. else
  111. {
  112. rotate(p, 1);
  113. remove(tree[p].ch[0], w);
  114. }
  115. }
  116. }
  117. }
  118. if (p)
  119. pushup(p);
  120. }
  121. int queryrank(int p, int k) // return the highest rank of value 'k'
  122. {
  123. if (!p)
  124. return 0;
  125. if (tree[p].w > k)
  126. return queryrank(tree[p].ch[0], k);
  127. else if (tree[p].w == k)
  128. return tree[tree[p].ch[0]].sz;
  129. else
  130. return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
  131. }
  132. int querynum(int p, int k) // return the value of kth rank node
  133. {
  134. if (tree[tree[p].ch[0]].sz + 1 == k)
  135. return tree[p].w;
  136. else if (tree[tree[p].ch[0]].sz + 1 < k)
  137. return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
  138. else
  139. return querynum(tree[p].ch[0], k);
  140. }
  141. int querypre(int p, int k) // return the prefix of value k
  142. {
  143. if (!p)
  144. return -2147483647;
  145. if (tree[p].w >= k)
  146. return querypre(tree[p].ch[0], k);
  147. else
  148. return max(tree[p].w, querypre(tree[p].ch[1], k));
  149. }
  150. int querysuf(int p, int k) // return the suffix of value k
  151. {
  152. if (!p)
  153. return 2147483647;
  154. if (tree[p].w <= k)
  155. return querysuf(tree[p].ch[1], k);
  156. else
  157. return min(tree[p].w, querysuf(tree[p].ch[0], k));
  158. }
  159. void listall(int p)
  160. {
  161. if (tree[p].ch[0])
  162. listall(tree[p].ch[0]);
  163. cerr << tree[p].w << ",sz=" << tree[p].num << " ";
  164. if (tree[p].ch[1])
  165. listall(tree[p].ch[1]);
  166. }
  167. }
  168. using Treap::listall;
  169. namespace SEG
  170. {
  171. struct segment
  172. {
  173. int l;
  174. int r;
  175. int root;
  176. };
  177. segment tree[MAXN * 8];
  178. void build(int p, int l, int r)
  179. {
  180. tree[p].l = l;
  181. tree[p].r = r;
  182. for (int i = l; i < r + 1; ++i)
  183. Treap::insert(tree[p].root, a[i]);
  184. if (l != r)
  185. {
  186. int mid = (l + r) / 2;
  187. build(p * 2, l, mid);
  188. build(p * 2 + 1, mid + 1, r);
  189. }
  190. }
  191. void modify(int p, int x, int y)
  192. {
  193. Treap::remove(tree[p].root, a[x]);
  194. Treap::insert(tree[p].root, y);
  195. if (tree[p].l == tree[p].r)
  196. return;
  197. int mid = (tree[p].l + tree[p].r) / 2;
  198. if (x > mid)
  199. modify(p * 2 + 1, x, y);
  200. else
  201. modify(p * 2, x, y);
  202. }
  203. int queryrank(int p, int l, int r, int k) // query the highest rank of value 'k'
  204. {
  205. if (tree[p].l > r || tree[p].r < l)
  206. return 0;
  207. if (tree[p].l >= l && tree[p].r <= r)
  208. return Treap::queryrank(tree[p].root, k);
  209. else
  210. return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
  211. }
  212. int querynum(int u, int v, int k) // query the value of kth num
  213. {
  214. int l = 0, r = 1e8;
  215. while (l < r)
  216. {
  217. int mid = (l + r + 1) / 2;
  218. if (queryrank(1, u, v, mid) < k)
  219. l = mid;
  220. else
  221. r = mid - 1;
  222. }
  223. return r;
  224. }
  225. int querypre(int p, int l, int r, int k)
  226. {
  227. if (tree[p].l > r || tree[p].r < l)
  228. return -2147483647;
  229. if (tree[p].l >= l && tree[p].r <= r)
  230. return Treap::querypre(tree[p].root, k);
  231. else
  232. return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
  233. }
  234. int querysuf(int p, int l, int r, int k)
  235. {
  236. if (tree[p].l > r || tree[p].r < l)
  237. return 2147483647;
  238. if (tree[p].l >= l && tree[p].r <= r)
  239. return Treap::querysuf(tree[p].root, k);
  240. else
  241. return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
  242. }
  243. }
  244. int read()
  245. {
  246. char ch = getchar();
  247. int x = 0, flag = 1;
  248. while (ch != '-' && (ch < '0' || ch > '9'))
  249. ch = getchar();
  250. if (ch == '-')
  251. {
  252. ch = getchar();
  253. flag = -1;
  254. }
  255. while (ch >= '0' && ch <= '9')
  256. {
  257. x = x * 10 + ch - '0';
  258. ch = getchar();
  259. }
  260. return x * flag;
  261. }
  262. int main()
  263. {
  264. n = read();
  265. m = read();
  266. for (int i = 1; i < n + 1; ++i)
  267. a[i] = read();
  268. SEG::build(1, 1, n);
  269. for (int i = 0; i < m; ++i)
  270. {
  271. int opt = read();
  272. if (opt == 3)
  273. {
  274. int x = read(), y = read();
  275. SEG::modify(1, x, y);
  276. a[x] = y;
  277. }
  278. else
  279. {
  280. int l = read(), r = read(), k = read();
  281. if (opt == 1)
  282. printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
  283. else if (opt == 2)
  284. printf("%d\n", SEG::querynum(l, r, k));
  285. else if (opt == 4)
  286. printf("%d\n", SEG::querypre(1, l, r, k));
  287. else
  288. printf("%d\n", SEG::querysuf(1, l, r, k));
  289. }
  290. }
  291. return 0;
  292. }

*主席树

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3834
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define mid (l+r)/2
  6. using namespace std;
  7. const int N = 200010;
  8. int n, q, m, cnt = 0;
  9. int a[N], b[N], T[N];
  10. int sum[N<<5], L[N<<5], R[N<<5];
  11. inline int build(int l, int r)
  12. {
  13. int rt = ++ cnt;
  14. sum[rt] = 0;
  15. if (l < r){
  16. L[rt] = build(l, mid);
  17. R[rt] = build(mid+1, r);
  18. }
  19. return rt;
  20. }
  21. inline int update(int pre, int l, int r, int x)
  22. {
  23. int rt = ++ cnt;
  24. L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre]+1;
  25. if (l < r){
  26. if (x <= mid) L[rt] = update(L[pre], l, mid, x);
  27. else R[rt] = update(R[pre], mid+1, r, x);
  28. }
  29. return rt;
  30. }
  31. inline int query(int u, int v, int l, int r, int k)
  32. {
  33. if (l >= r) return l;
  34. int x = sum[L[v]] - sum[L[u]];
  35. if (x >= k) return query(L[u], L[v], l, mid, k);
  36. else return query(R[u], R[v], mid+1, r, k-x);
  37. }
  38. int main()
  39. {
  40. scanf("%d%d", &n, &q);
  41. for (int i = 1; i <= n; i ++){
  42. scanf("%d", &a[i]);
  43. b[i] = a[i];
  44. }
  45. sort(b+1, b+1+n);
  46. m = unique(b+1, b+1+n)-b-1;
  47. T[0] = build(1, m);
  48. for (int i = 1; i <= n; i ++){
  49. int t = lower_bound(b+1, b+1+m, a[i])-b;
  50. T[i] = update(T[i-1], 1, m, t);
  51. }
  52. while (q --){
  53. int x, y, z;
  54. scanf("%d%d%d", &x, &y, &z);
  55. int t = query(T[x-1], T[y], 1, m, z);
  56. printf("%d\n", b[t]);
  57. }
  58. return 0;
  59. }

【简介】

LCT是一种解决动态树问题的方法,由tarjan~~(为什么又是他)~~在1982年提出,最原始的论文在这里,在论文中,tarjan除了介绍了均摊复杂度为的LCT之外,还介绍了一种严格的算法。

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3690
  2. #include<bits/stdc++.h>
  3. #define N 300005
  4. using namespace std;
  5. int n,m,val[N];
  6. struct Link_Cut_Tree{
  7. int top,c[N][2],fa[N],xr[N],q[N],rev[N];
  8. inline void pushup(int x)
  9. {
  10. xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];
  11. }
  12. inline void pushdown(int x)
  13. {
  14. int l=c[x][0],r=c[x][1];
  15. if(rev[x])
  16. {
  17. rev[l]^=1;
  18. rev[r]^=1;
  19. rev[x]^=1;
  20. swap(c[x][0],c[x][1]);
  21. }
  22. }
  23. inline bool isroot(int x)
  24. {
  25. return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
  26. }
  27. void rotate(int x)
  28. {
  29. int y=fa[x],z=fa[y],l,r;
  30. if(c[y][0]==x)l=0;
  31. else l=1;
  32. r=l^1;
  33. if(!isroot(y))
  34. {
  35. if(c[z][0]==y)c[z][0]=x;
  36. else c[z][1]=x;
  37. }
  38. fa[x]=z;
  39. fa[y]=x;
  40. fa[c[x][r]]=y;
  41. c[y][l]=c[x][r];
  42. c[x][r]=y;
  43. pushup(y);
  44. pushup(x);
  45. }
  46. void splay(int x)
  47. {
  48. top=1;
  49. q[top]=x;
  50. for(int i=x; !isroot(i); i=fa[i])q[++top]=fa[i];
  51. for(int i=top; i; i--)pushdown(q[i]);
  52. while(!isroot(x))
  53. {
  54. int y=fa[x],z=fa[y];
  55. if(!isroot(y))
  56. {
  57. if((c[y][0]==x)^(c[z][0]==y))rotate(x);
  58. else rotate(y);
  59. }
  60. rotate(x);
  61. }
  62. }
  63. void access(int x)
  64. {
  65. for(int t=0; x; t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);
  66. }
  67. void makeroot(int x)
  68. {
  69. access(x);
  70. splay(x);
  71. rev[x]^=1;
  72. }
  73. int find(int x)
  74. {
  75. access(x);
  76. splay(x);
  77. while(c[x][0])x=c[x][0];
  78. return x;
  79. }
  80. void split(int x,int y)
  81. {
  82. makeroot(x);
  83. access(y);
  84. splay(y);
  85. }
  86. void cut(int x,int y)
  87. {
  88. split(x,y);
  89. if(c[y][0]==x)c[y][0]=0,fa[x]=0;
  90. }
  91. void link(int x,int y)
  92. {
  93. makeroot(x);
  94. fa[x]=y;
  95. }
  96. } T;
  97. inline int read()
  98. {
  99. int f=1,x=0;
  100. char ch;
  101. do
  102. {
  103. ch=getchar();
  104. if(ch=='-')f=-1;
  105. }
  106. while(ch<'0'||ch>'9');
  107. do
  108. {
  109. x=x*10+ch-'0';
  110. ch=getchar();
  111. }
  112. while(ch>='0'&&ch<='9');
  113. return f*x;
  114. }
  115. int main()
  116. {
  117. n=read();
  118. m=read();
  119. for(int i=1; i<=n; i++)val[i]=read(),T.xr[i]=val[i];
  120. while(m--)
  121. {
  122. int opt=read();
  123. if(opt==0)
  124. {
  125. int x=read(),y=read();
  126. T.split(x,y);
  127. printf("%d\n",T.xr[y]);
  128. }
  129. if(opt==1)
  130. {
  131. int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
  132. if(xx!=yy)T.link(x,y);
  133. }
  134. if(opt==2)
  135. {
  136. int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
  137. if(xx==yy)T.cut(x,y);
  138. }
  139. if(opt==3)
  140. {
  141. int x=read(),y=read();
  142. T.access(x);
  143. T.splay(x);
  144. val[x]=y;
  145. T.pushup(x);
  146. }
  147. }
  148. return 0;
  149. }

复杂度


STL

Queue

【代码示例】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3187/
  5. #include<cstdio>
  6. #include<queue>
  7. using namespace std;
  8. queue<int> q;
  9. int n,opt,k;
  10. int main()
  11. {
  12. scanf("%d",&n);
  13. for(int i=1;i<=n;i++)
  14. {
  15. scanf("%d",&opt);
  16. if(opt==1) scanf("%d",&k),q.push(k);
  17. else if(opt==2) q.pop();
  18. else printf("%d\n",q.front());
  19. }
  20. return 0;
  21. }

Stack

【代码示例】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/3139/
  5. #include<cstdio>
  6. #include<stack>
  7. using namespace std;
  8. int n,opt,k;
  9. stack<int> s;
  10. int main()
  11. {
  12. scanf("%d",&n);
  13. for(int i=1;i<=n;i++)
  14. {
  15. scanf("%d",&opt);
  16. if(opt==1) scanf("%d",&k),s.push(k);
  17. else if(opt==2) s.pop();
  18. else printf("%d\n",s.top());
  19. }
  20. return 0;
  21. }

Priority_queue

【代码示例】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3378#sub
  5. #include<cstdio>
  6. #include<queue>
  7. #include<cctype>
  8. using namespace std;
  9. priority_queue<int,vector<int>,greater<int> > q;
  10. int m,a,b;
  11. int readin()
  12. {
  13. int res=0;char ch=0;bool XX=false;
  14. for(;!isdigit(ch);ch=getchar())
  15. if(ch=='-') XX=true;
  16. for(;isdigit(ch);ch=getchar()) res=(res<<3)+(res<<1)+ch-'0';
  17. return (XX?-res:res);
  18. }
  19. void writeout(int res)
  20. {
  21. if(res<0) {putchar('-');res=-res;}
  22. if(res>9) writeout(res/10);
  23. putchar(res%10+'0');
  24. }
  25. int main()
  26. {
  27. m=readin();
  28. for(int i=1;i<=m;i++)
  29. {
  30. a=readin();
  31. if(a==1)
  32. {
  33. b=readin();
  34. q.push(b);
  35. }
  36. else if(a==2) writeout(q.top()),putchar('\n');
  37. else q.pop();
  38. }
  39. return 0;
  40. }

Deque

【代码示例】

  1. #include <iostream>
  2. #include<deque>
  3. using namespace std;
  4. int main()
  5. {
  6. deque<int> mydeque (7,6); // 初始化deque为7个int,每个int值为6
  7. mydeque.push_front(2); //插入头
  8. mydeque.push_back(3); //插入尾
  9. cout << "mydeque size: " << mydeque.size() << endl;
  10. cout << "mydeque contains:";
  11. for (unsigned i=0; i<mydeque.size();i++) cout << " " << mydeque[i];
  12. cout << endl;
  13. return 0;
  14. }

Sort

【代码示例】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:http://codevs.cn/problem/1076/
  5. #include<cstdio>
  6. #include<algorithm>
  7. int n,a[100001];
  8. int main()
  9. {
  10. scanf("%d",&n);
  11. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  12. std::sort(a+1,a+n+1);
  13. for(int i=1;i<=n;i++) printf("%d%c",a[i],i==n?'\n':' ');
  14. return 0;
  15. }

Map

【代码示例】

  1. /*
  2. copy by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3370
  5. #include<iostream>
  6. #include<string>
  7. #include<map>
  8. using namespace std;
  9. map<string,int>p;
  10. int n;
  11. string s;
  12. int main()
  13. {
  14. cin>>n;
  15. for(int i=1;i<=n;i++)
  16. {
  17. cin>>s;
  18. p[s]++;
  19. }
  20. cout<<p.size();
  21. return 0;
  22. }

Strstr

【代码示例】

  1. #include <string.h>
  2. #include <iostream.h>
  3. void main(void)
  4. {
  5. char sStr1[100],sStr2[100],sStr3[100];
  6. sStr1[0] = sStr2[0] = sStr3[0] = '\0';
  7. strcpy(sStr1,"Golden Global View");
  8. strcpy(sStr2,"bal");
  9. strcpy(sStr3,"Hell");
  10. char *p = strstr(sStr1,sStr2); //从sStr1中查找bal
  11. char *p2 = strstr(sStr1,sStr3); //从sStr1中查找Hell
  12. cout<<(p==NULL?"NULL":p)<<endl;
  13. cout<<(p2==NULL?"NULL":p2)<<endl;
  14. }

Find

【代码示例】

  1. #include <iostream> // std::cout
  2. #include <algorithm> // std::find
  3. #include <vector> // std::vector
  4. int main () {
  5. // using std::find with array and pointer:
  6. int myints[] = { 10, 20, 30, 40 };
  7. int * p;
  8. p = std::find (myints, myints+4, 30);
  9. if (p != myints+4)
  10. std::cout << "Element found in myints: " << *p << '\n';
  11. else
  12. std::cout << "Element not found in myints\n";
  13. // using std::find with vector and iterator:
  14. std::vector<int> myvector (myints,myints+4);
  15. std::vector<int>::iterator it;
  16. it = find (myvector.begin(), myvector.end(), 30);
  17. if (it != myvector.end())
  18. std::cout << "Element found in myvector: " << *it << '\n';
  19. else
  20. std::cout << "Element not found in myvector\n";
  21. return 0;
  22. }

Set

【代码示例】

  1. #include <iostream>
  2. #include <set>
  3. using namespace std;
  4. int main()
  5. {
  6. /*type of the collection:
  7. *-no duplicates
  8. *-elements are integral values
  9. *-descending order
  10. */
  11. typedef set<int,greater<int> > IntSet;
  12. IntSet coll1; // empty set container
  13. //insert elements in random order
  14. coll1.insert(4);
  15. coll1.insert(3);
  16. coll1.insert(5);
  17. coll1.insert(1);
  18. coll1.insert(6);
  19. coll1.insert(2);
  20. coll1.insert(5);
  21. //iterate over all elements and print them
  22. IntSet::iterator pos;
  23. for (pos = coll1.begin(); pos != coll1.end(); ++pos) {
  24. cout << *pos << ' ';
  25. }
  26. cout << endl;
  27. //insert 4 again and process return value
  28. pair<IntSet::iterator,bool> status = coll1.insert(4);
  29. if (status.second) {
  30. cout << "4 inserted as element "
  31. << distance (coll1.begin(),status. first) + 1
  32. << endl;
  33. }
  34. else {
  35. cout << "4 already exists" << endl;
  36. }
  37. //assign elements to another set with ascending order
  38. set<int> coll2(coll1.begin(),
  39. coll1.end());
  40. //print all elements of the copy
  41. copy (coll2.begin(), coll2.end(),
  42. ostream_iterator<int>(cout," "));
  43. cout << endl;
  44. //remove all elements up to element with value 3
  45. coll2.erase (coll2.begin(), coll2.find(3));
  46. //remove all elements with value 5
  47. int num;
  48. num = coll2.erase (5);
  49. cout << num << " element(s) removed" << endl;
  50. //print all elements
  51. copy (coll2.begin(), coll2.end(),
  52. ostream_iterator<int>(cout," "));
  53. cout << endl;
  54. }

*Multiset

【代码示例】

  1. #include <iostream>
  2. #include <set>
  3. using namespace std;
  4. int main()
  5. {
  6. /*type of the collection:
  7. *-duplicates allowed
  8. *-elements are integral values
  9. *-descending order
  10. */
  11. typedef multiset<int,greater<int> > IntSet;
  12. IntSet coll1, // empty multiset container
  13. //insert elements in random order
  14. coll1.insert(4);
  15. coll1.insert(3);
  16. coll1.insert(5);
  17. coll1.insert(l);
  18. coll1.insert(6);
  19. coll1.insert(2);
  20. coll1.insert(5);
  21. //iterate over all elements and print them
  22. IntSet::iterator pos;
  23. for (pos = coll1.begin(); pos != coll1.end(); ++pos) {
  24. cout << *pos << ' ';
  25. }
  26. cout << endl;
  27. //insert 4 again and process return value
  28. IntSet::iterator ipos = coll1.insert(4);
  29. cout << "4 inserted as element "
  30. << distance (coll1.begin(),ipos) + 1
  31. << endl;
  32. //assign elements to another multiset with ascending order
  33. multiset<int> coll2(coll1.begin(),
  34. coll1.end());
  35. //print all elements of the copy
  36. copy (coll2.begin(), coll2.end(),
  37. ostream_iterator<int>(cout," "));
  38. cout << endl;
  39. //remove all elements up to element with value 3
  40. coll2.erase (coll2.begin(), coll2.find(3));
  41. //remove all elements with value 5
  42. int num;
  43. num = coll2.erase (5);
  44. cout << num << " element(s) removed" << endl;
  45. //print all elements
  46. copy (coll2.begin(), coll2.end(),
  47. ostream_iterator<int>(cout," "));
  48. cout << endl;
  49. }

*Pb_ds库

*配对堆

【代码示例】
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<queue>
  6. #include<ext/pb_ds/priority_queue.hpp>
  7. #define pir pair<ll,int>
  8. using namespace std;
  9. typedef long long ll;
  10. //typedef std::priority_queue<pir,vector<pir>,greater<pir> > heap;
  11. //3840 ms
  12. typedef __gnu_pbds::priority_queue<pir,greater<pir> > heap;//默认是pairing_heap_tag
  13. //3304 ms
  14. const int N=1e6+5,M=1e7+5;
  15. const ll INF=1e15;
  16. inline int read(){
  17. char c=getchar();int x=0,f=1;
  18. while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
  19. while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
  20. return x*f;
  21. }
  22. int n,m,T,rxa,rxc,rya,ryc,rp,a,b;
  23. int x,y,z;
  24. struct node{
  25. int v,w,next;
  26. }e[M];
  27. int cnt,head[N];ll dis[N];bool vis[N];
  28. inline void add(int u,int v,int w){
  29. e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt;
  30. }
  31. heap q;
  32. inline void dijkstra(){
  33. int S;
  34. for(int i=1;i<=n;i++) dis[i]=INF;
  35. dis[S=1]=0;
  36. q.push(make_pair(dis[S],S));
  37. while(!q.empty()){
  38. pir t=q.top();q.pop();
  39. int x=t.second;
  40. if(vis[x]) continue;
  41. vis[x]=1;
  42. for(int i=head[x];i;i=e[i].next){
  43. int v=e[i].v;
  44. if(!vis[v]&&dis[v]>dis[x]+e[i].w){
  45. dis[v]=dis[x]+e[i].w;
  46. q.push(make_pair(dis[v],v));
  47. }
  48. }
  49. }
  50. }
  51. int main(){
  52. n=read();m=read();
  53. T=read();rxa=read();rxc=read();rya=read();ryc=read();rp=read();
  54. m=m-T;
  55. x=rxc%rp;y=ryc%rp;
  56. a=min(x%n+1,y%n+1);
  57. b=max(y%n+1,y%n+1);
  58. while(T--) add(a,b,100000000-100*a);
  59. while(m--) x=read(),y=read(),z=read(),add(x,y,z);
  60. dijkstra();
  61. printf("%lld",dis[n]);
  62. return 0;
  63. }

*平衡树

【代码示例】
  1. #include<cstdio>
  2. #include<ext/pb_ds/assoc_container.hpp>
  3. //pb_ds库这次内置了红黑树(red-black tree)、伸展树(splay tree)和排序向量树(ordered-vector tree,没找到通用译名,故自行翻译)。
  4. //这些封装好的树都支持插入(insert)、删除(erase)、求kth(find_by_order)、求rank(order_of_key)操作,O(logn)内完成
  5. using namespace std;
  6. using namespace __gnu_pbds;
  7. int read(){
  8. int x=0,f=1;char ch=getchar();
  9. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  10. while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
  11. return x*f;
  12. }
  13. tree<int,null_mapped_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>bbt;
  14. //SPOJG++版本稍旧(4.3.2),需要写成null_mapped_type才可以(高级版本可以写null_type)
  15. char in(){
  16. for(char ch=getchar();;ch=getchar()) if(ch>='A'&&ch<='Z') return ch;
  17. }
  18. int main(){
  19. char c;int x;
  20. for(int T=read();T--;){
  21. c=in();x=read();
  22. if(c=='I'){
  23. bbt.insert(x);
  24. }
  25. else if(c=='D'){
  26. bbt.erase(x);
  27. }
  28. else if(c=='K'){
  29. if(x<=bbt.size())
  30. printf("%d\n",*bbt.find_by_order(x-1));
  31. else
  32. puts("invalid");
  33. }
  34. else{
  35. printf("%d\n",bbt.order_of_key(x));
  36. }
  37. }
  38. return 0;
  39. }
  1. #include<cstdio>
  2. #include<ext/pb_ds/assoc_container.hpp>
  3. #include<ext/pb_ds/tree_policy.hpp>
  4. #pragma GCC optimize("02")
  5. using namespace std;
  6. using namespace __gnu_pbds;
  7. typedef long long ll;
  8. ll read(){
  9. ll x=0,f=1;char ch=getchar();
  10. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  11. while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
  12. return x*f;
  13. }
  14. typedef __gnu_pbds::tree<ll,null_mapped_type,less<ll>,rb_tree_tag,tree_order_statistics_node_update> T;
  15. T bbt;ll ans;
  16. int main(){
  17. ll n=read();
  18. for(ll i=1,o,k;i<=n;i++){
  19. o=read();k=read();
  20. if(o==1) bbt.insert((k<<20)+i);else
  21. if(o==2) bbt.erase(bbt.lower_bound(k<<20));else
  22. if(o==3) printf("%d\n",bbt.order_of_key(k<<20)+1);else
  23. {
  24. if(o==4) ans=*bbt.find_by_order(k-1);else
  25. if(o==5) ans=*--bbt.lower_bound(k<<20);else
  26. if(o==6) ans=*bbt.lower_bound((k+1)<<20);
  27. printf("%lld\n",ans>>20);
  28. }
  29. }
  30. return 0;
  31. }

字符串算法

KMP匹配算法

【简介】

算法是一种改进的字符串匹配算法,由同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称算法)。
算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
具体实现就是实现一个函数,函数本身包含了模式串的局部匹配信息。时间复杂度

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3375
  5. #include<cstdio>
  6. #include<cstring>
  7. #define N 1000001
  8. char str[N],s[N];
  9. int next[N],n,m;
  10. int main()
  11. {
  12. scanf("%s%s",str+1,s+1);//这里一定要用scanf,由于gets在Linux和Windows下换行符不同,用gets这里会WA掉(血的教训!!!)
  13. n=strlen(str+1);m=strlen(s+1);
  14. int j=0;
  15. for(int i=2;i<=m;i++)
  16. {
  17. while(j&&s[i]!=s[j+1]) j=next[j];
  18. if(s[i]==s[j+1]) j++;
  19. next[i]=j;
  20. }
  21. j=0;
  22. for(int i=1;i<=n;i++)
  23. {
  24. while(j&&s[j+1]!=str[i]) j=next[j];
  25. if(str[i]==s[j+1]) j++;
  26. if(j==m) printf("%d\n",i-m+1),j=next[j];
  27. }
  28. for(int i=1;i<=m;i++)
  29. printf("%d%c",next[i],i==m?'\n':' ');
  30. return 0;
  31. }

复杂度

Manacher算法

【简介】

Manachar算法主要是处理字符串中关于回文串的问题的,它可以在的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符之间加入一个特定的特殊字符,因此原本长度为偶数的回文串就成了以中间特殊字符为中心的奇数长度的回文串了。

【代码实现】

  1. /*
  2. Coded by Apojacsleam
  3. */
  4. //problemID:https://www.luogu.org/problemnew/show/P3805
  5. #include<cstdio>
  6. #include<cstring>
  7. #include<algorithm>
  8. using namespace std;
  9. #define N 11000010
  10. char str[N],changedstr[N<<1];
  11. int p[N<<1];
  12. int change()
  13. {
  14. int j=0,len=strlen(str+1);
  15. changedstr[j++]='$';
  16. changedstr[j++]='#';
  17. for(int i=1;i<=len;i++) changedstr[j++]=str[i],changedstr[j++]='#';
  18. changedstr[j]='%';
  19. return j;
  20. }
  21. int Manacher()
  22. {
  23. int n=change(),maxID=1,maxPOS=1,ans=1;
  24. for(int i=1;i<=n;i++)
  25. {
  26. if(i<maxPOS) p[i]=min(maxPOS-i,p[maxID*2-i]);
  27. else p[i]=1;
  28. while(changedstr[i-p[i]]==changedstr[i+p[i]]) p[i]++;
  29. if(p[i]+i>maxPOS)
  30. {
  31. maxID=i;
  32. maxPOS=p[i]+i;
  33. }
  34. ans=max(ans,p[i]-1);
  35. }
  36. return ans;
  37. }
  38. int main()
  39. {
  40. scanf("%s",str+1);
  41. printf("%d",Manacher());
  42. return 0;
  43. }

复杂度

字符串hash

【简介】

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

【代码实现】

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. typedef unsigned long long ull;
  6. ull base=131;
  7. struct data
  8. {
  9. ull x,y;
  10. }a[10010];
  11. char s[10010];
  12. int n,ans=1;
  13. ull mod1=19260817;
  14. ull mod2=19660813;
  15. ull hash1(char s[])
  16. {
  17. int len=strlen(s);
  18. ull ans=0;
  19. for (int i=0;i<len;i++)
  20. ans=(ans*base+(ull)s[i])%mod1;
  21. return ans;
  22. }
  23. ull hash2(char s[])
  24. {
  25. int len=strlen(s);
  26. ull ans=0;
  27. for (int i=0;i<len;i++)
  28. ans=(ans*base+(ull)s[i])%mod2;
  29. return ans;
  30. }
  31. bool comp(data a,data b)
  32. {
  33. return a.x<b.x;
  34. }
  35. signed main()
  36. {
  37. scanf("%d",&n);
  38. for (int i=1;i<=n;i++)
  39. {
  40. scanf("%s",s);
  41. a[i].x=hash1(s);
  42. a[i].y=hash2(s);
  43. }
  44. sort(a+1,a+n+1,comp);
  45. for (int i=2;i<=n;i++)
  46. if (a[i].x!=a[i-1].x || a[i-1].y!=a[i].y)
  47. ans++;
  48. printf("%d\n",ans);
  49. }

高精度计算

高精度加法

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P1601
  2. #include<cstdio>
  3. #include<cstring>
  4. #define max(x,y) (x)>(y)?(x):(y)
  5. char a1[1000],b1[1000];
  6. int a[1000],b[1000],c[1000];
  7. int main()
  8. {
  9. scanf("%s",a1);scanf("%s",b1);
  10. if(a1[0]==48&&b1[0]==48) return 0*puts("0");
  11. int lena=strlen(a1),lenb=strlen(b1);
  12. for(int i=0;i<lena;i++)
  13. a[lena-i-1]=int(a1[i]-48);
  14. for(int i=0;i<lenb;i++)
  15. b[lenb-i-1]=int(b1[i]-48);//倒序输入便于进位
  16. int m=max(lena,lenb);
  17. for(int i=0;i<m;i++)
  18. {
  19. c[i]+=a[i]+b[i];//不能直接赋值,要加上前面的进位
  20. while(c[i]>=10)
  21. {
  22. c[i+1]++;
  23. c[i]-=10;
  24. }
  25. }
  26. m++;
  27. while(c[m]==0) m--; //删除前导0
  28. for(int i=m;i>=0;i--) putchar(c[i]+48);
  29. return 0;
  30. }

复杂度

高精度减法

【代码实现】
  1. //problemID:https://www.luogu.org/problemnew/show/P2142
  2. #include <iostream>
  3. #include <cmath>
  4. #include <cstring>
  5. using namespace std;
  6. int main()
  7. {
  8. char a[100010],b[100010];//设两个字符串
  9. cin>>a>>b;//读入两个字符串
  10. int c[100010],d[100010],h[100010],n1,n2,i,f=0,l=0;
  11. n1=strlen(a);//求字符串长度
  12. n2=strlen(b);
  13. for(i=0;i<n1/2;i++) swap(a[i],a[n1-1-i]);
  14. for(i=0;i<n2/2;i++) swap(b[i],b[n2-1-i]);
  15. for(i=0;i<n1;i++) c[i]=a[i]-'0';//把字符串a的字符转化为数字存到数组c当中。其中“-‘0’”为转换方法
  16. for(i=0;i<n2;i++) d[i]=b[i]-'0';
  17. if(n2>n1)//这一步是判断那个数长,哪个就大,就用哪个做被减数存到数组c中,哪个小就存到d中
  18. {
  19. for(i=0;i<n2;i++) swap(c[i],d[i]);//把两数交换,swap为交换函数
  20. f=1;//设一个旗帜,以后如果f=1就说明这数被减数比减数小,是负数。
  21. }
  22. if(n1>n2) swap(n1,n2); //取长的做for循环条件
  23. for(i=0;i<n2;i++) h[i]=c[i]-d[i];
  24. for(i=0;i<n2;i++)//这部就是借位
  25. {
  26. if(h[i]<0)
  27. {
  28. h[i]=10+h[i];
  29. h[i+1]--;
  30. }
  31. }
  32. if(f==1) cout<<"-";//如果f等于一也就是结果为负数,打印“-”
  33. for(i=n2-1;i>=0;i--)//这步很重要! 这是在输出时把首位的0都去掉
  34. {
  35. if(l==0)//设了一个l,如果l为0意味着还没有碰到非零数,也就是有0就要去掉的0
  36. {
  37. if(h[i]!=0) //如果这数不为零
  38. {
  39. l=1;//l=1表明碰到了非零数了以后的0有实际意义要打印出来
  40. cout<<h[i];//打印此数
  41. continue;//然后跳出本次循环
  42. }
  43. }
  44. if(l!=0)//如果l不等于0,就说明这时的0有实际意义,要打印出来
  45. {
  46. cout<<h[i];
  47. }
  48. }
  49. }

复杂度

高精度乘法

高精度乘单精度
【代码实现】
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int L=100005;
  6. int na[L];
  7. string mul(string a,int b) //高精度a乘单精度b
  8. {
  9. string ans;
  10. int La=a.size();
  11. fill(na,na+L,0);
  12. for(int i=La-1;i>=0;i--) na[La-i-1]=a[i]-'0';
  13. int w=0;
  14. for(int i=0;i<La;i++) na[i]=na[i]*b+w,w=na[i]/10,na[i]=na[i]%10;
  15. while(w) na[La++]=w%10,w/=10;
  16. La--;
  17. while(La>=0) ans+=na[La--]+'0';
  18. return ans;
  19. }
  20. int main()
  21. {
  22. string a;
  23. int b;
  24. while(cin>>a>>b) cout<<mul(a,b)<<endl;
  25. return 0;
  26. }

复杂度

高精度乘高精度
朴素模拟
  1. //problemID:http://codevs.cn/problem/3117/
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. char num1[505],num2[505];
  5. int com1[505],com2[506],re[1005],ans[1005];
  6. int main()
  7. {
  8. scanf("%s",&num1);
  9. scanf("%s",&num2);
  10. int len1=strlen(num1),len2=strlen(num2);
  11. for(int i=len1-1;i>=0;i--)
  12. {
  13. com1[len1-i-1]=num1[i]-48;
  14. }
  15. for(int i=len2-1;i>=0;i--)
  16. {
  17. com2[len2-i-1]=num2[i]-48;
  18. }
  19. for(int i=0;i<=len1-1;i++)
  20. {
  21. for(int j=0;j<=len2-1;j++)
  22. {
  23. re[i+j]=com1[i]*com2[j];
  24. }
  25. for(int i=0;i<=1000;i++)
  26. {
  27. int p=re[i]%10;
  28. if(re[i]>=10)
  29. re[i+1]+=re[i]/10;
  30. re[i]=p;
  31. }
  32. for(int j=0;j<=1000;j++)
  33. {
  34. ans[j]+=re[j];
  35. }
  36. memset(re,0,sizeof(re));
  37. }
  38. for(int i=0;i<=1000;i++)
  39. {
  40. int p=ans[i]%10;
  41. if(ans[i]>=10)
  42. ans[i+1]+=ans[i]/10;
  43. ans[i]=p;
  44. }
  45. int t;
  46. for(int i=1005;i>=0;i--)
  47. {
  48. if(ans[i]!=0)
  49. {
  50. t=i;
  51. break;
  52. }
  53. }
  54. for(int i=t;i>=0;i--)
  55. {
  56. printf("%d",ans[i]);
  57. }
  58. return 0;
  59. }

复杂度

*FFT实现
  1. #include<cstdio>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<iostream>
  6. using namespace std;
  7. #define N (1<<18)
  8. struct cmpx{
  9. double r,i;
  10. cmpx operator + (const cmpx &a)const { return (cmpx){ r+a.r,i+a.i }; }
  11. cmpx operator - (const cmpx &a)const { return (cmpx){ r-a.r,i-a.i }; }
  12. cmpx operator * (const cmpx &a)const { return (cmpx){ r*a.r-i*a.i,r*a.i+i*a.r }; }
  13. };
  14. int rev[N];cmpx A[N],a[N],b[N],c[N];
  15. int L,n;
  16. void DFT(cmpx a[],int f){
  17. for(int i=0;i<L;i++) A[i]=a[rev[i]];
  18. for(int i=0;i<L;i++) a[i]=A[i];
  19. for(int i=2;i<=L;i<<=1){
  20. cmpx wi=(cmpx){ cos(2.0*M_PI/i),f*sin(2.0*M_PI/i) };
  21. for(int k=0;k<L;k+=i){
  22. cmpx w=(cmpx){ 1.0,0.0 },x,y;
  23. for(int j=0;j<i/2;j++){
  24. x=a[k+j],y=w*a[k+j+i/2];
  25. a[k+j]=x+y,a[k+j+i/2]=x-y;
  26. w=w*wi;
  27. }
  28. }
  29. }if(f==-1) for(int i=0;i<L;i++) a[i].r/=L;
  30. }
  31. void FFT(cmpx a[],cmpx b[],cmpx c[]){
  32. DFT(a,1),DFT(b,1);
  33. for(int i=0;i<L;i++) c[i]=a[i]*b[i];
  34. DFT(c,-1);
  35. }
  36. void init(int tmp){
  37. for(n=0,L=1;L<tmp;L<<=1,n++);
  38. n++,L<<=1;
  39. for(int i=0;i<L;i++) for(int t=i,j=0;j<n;j++) rev[i]<<=1,rev[i]|=t&1,t>>=1;
  40. }
  41. int ans[N];
  42. int main(){
  43. int l1=0,l2=0;char ch=getchar();
  44. while(ch>'9'||ch<'0') ch=getchar();while(ch>='0'&&ch<='9') a[l1].r=ch-'0',a[l1++].i=0.0,ch=getchar();
  45. while(ch>'9'||ch<'0') ch=getchar();while(ch>='0'&&ch<='9') b[l2].r=ch-'0',b[l2++].i=0.0,ch=getchar();
  46. reverse(a,a+l1),reverse(b,b+l2),init(max(l1,l2));
  47. FFT(a,b,c);
  48. for(int i=0;i<L;i++) ans[i]=c[i].r+0.5;
  49. for(int i=0;i<L;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
  50. L=l1+l2+1;while(!ans[L]&&L>0) L--;
  51. for(int i=L;~i;i--) putchar(ans[i]+'0');putchar('\n');
  52. return 0;
  53. }

复杂度

高精度除法

高精度除单精度
【代码实现】
  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. string div(string a,int b)//高精度a除以单精度b
  5. {
  6. string r,ans;
  7. int d=0;
  8. if(a=="0") return a;//特判
  9. for(int i=0;i<a.size();i++)
  10. {
  11. r+=(d*10+a[i]-'0')/b+'0';//求出商
  12. d=(d*10+(a[i]-'0'))%b;//求出余数
  13. }
  14. int p=0;
  15. for(int i=0;i<r.size();i++)
  16. if(r[i]!='0') {p=i;break;}
  17. return r.substr(p);
  18. }
  19. int main()
  20. {
  21. string a;
  22. int b;
  23. while(cin>>a>>b)
  24. {
  25. cout<<div(a,b)<<endl;
  26. }
  27. return 0;
  28. }

复杂度

高精度除高精度
【代码实现】
  1. #include <iostream>
  2. #include <cstring>
  3. #define ref(i,x,y) for (int i=x; i<=y; i++)
  4. #define def(i,x,y) for (int i=x; i>=y; i--)
  5. #define chstr(a,x,lenx) lenx=strlen(a);ref(i,1,lenx)x[i]=a[lenx-i]-48
  6. #define tl(f) t[f][0]
  7. using namespace std;
  8. char a[501],b[501];
  9. int x[501],y[501],ans[501],t[10][501],lenx,leny;
  10. int main()
  11. {
  12. cin>>a>>b;
  13. chstr(a,x,lenx);
  14. chstr(b,y,leny);
  15. tl(1)=leny;
  16. ref(i,1,leny)t[1][i]=y[i];
  17. ref(i,2,9)
  18. {
  19. tl(i)=tl(i-1);
  20. ref(j,1,tl(i))
  21. t[i][j]=t[i-1][j]+y[j];
  22. ref(j,1,tl(i))if(t[i][j]>9){
  23. t[i][j+1]++;t[i][j]-=10;}
  24. if(t[i][tl(i)+1]!=0)tl(i)++;
  25. }
  26. int lent=lenx,anslen=lenx;
  27. def(i,lenx,1)
  28. {
  29. int an=0,len1=max(lent-i+1,0);
  30. def(j,9,0)
  31. {
  32. bool bo=1;
  33. def(k,max(len1,tl(j)),1)
  34. if(t[j][k]>x[i+k-1]){
  35. bo=0;break;}
  36. else if(t[j][k]<x[i+k-1])
  37. break;
  38. if (bo){
  39. an=j;break;}
  40. }
  41. ans[i]=an;
  42. ref(j,1,tl(an))
  43. x[j+i-1]-=t[an][j];
  44. ref(j,i,i+tl(an)-1)if (x[j]<0){
  45. x[j]+=10;x[j+1]--;}
  46. while(x[lent]==0)lent--;
  47. }
  48. while((ans[anslen]==0)&&(anslen>1))anslen--;
  49. def(i,anslen,1)cout<<ans[i];
  50. }

复杂度

*高精度开根

【代码实现】
  1. #include<iostream>
  2. using namespace std;
  3. #define MAX 1001
  4. struct NEW_NUM
  5. {
  6. int num[MAX];
  7. int start;
  8. int end;
  9. NEW_NUM(){
  10. for (int i = 0; i < MAX; i++){
  11. num[i] = 0;
  12. }
  13. }
  14. };
  15. void calculate(NEW_NUM &temp);
  16. void add(NEW_NUM &temp, int num);
  17. void sub(NEW_NUM &a, int a_start, int a_end, NEW_NUM b, int b_start, int b_end);
  18. bool comp(NEW_NUM a, int a_start, int a_end, NEW_NUM b, int b_start, int b_end);
  19. int main(){
  20. char ch[MAX];
  21. NEW_NUM a, b;
  22. cin >> ch;
  23. int i;
  24. a.start = 1;
  25. for (i = 0; i <= MAX-1; i++){
  26. if (ch[i] == '\0')break;
  27. a.num[a.start+i] = ch[i] - '0';
  28. }
  29. a.end = i;
  30. int xx, yy;
  31. xx = yy = 1;
  32. b.start = 1;
  33. if (a.end % 2 == 0){
  34. int te = a.num[xx] * 10 + a.num[xx + 1];
  35. for (int i = 0; i <= 10; i++){
  36. if (i*i > te){
  37. b.num[yy] = i - 1;
  38. a.num[xx + 1] += a.num[xx]*10-(i-1)*(i-1);
  39. a.num[xx] = 0;
  40. if (a.num[xx + 1]>= 10){
  41. a.num[xx] += a.num[xx + 1] / 10;
  42. a.num[xx+1] = a.num[xx+1] % 10;
  43. }
  44. yy++;
  45. break;
  46. }
  47. }
  48. xx = xx + 2;
  49. }
  50. else{
  51. int te = a.num[xx];
  52. for (int i = 0; i <= 10; i++){
  53. if (i*i > te){
  54. b.num[yy] = i - 1;
  55. a.num[xx] -= (i-1)*(i-1);
  56. yy++;
  57. break;
  58. }
  59. }
  60. xx = xx + 1;
  61. }
  62. b.end = 1;
  63. while (xx+1<=a.end){
  64. NEW_NUM temp;
  65. temp.start = 3;
  66. for (int i = 1; i < yy; i++){
  67. temp.num[temp.start+i-1] = b.num[i];
  68. }
  69. b.num[yy] = 0;
  70. b.end = yy;
  71. temp.end = temp.start+yy-1;
  72. calculate(temp);
  73. for (int i = 1; i < 10; i++){
  74. if (i == 1)add(temp, 1);
  75. else add(temp, 2);
  76. if (comp(a, a.start, xx+1, temp, temp.start, temp.end)){
  77. sub(a, a.start, xx+1, temp, temp.start, temp.end);
  78. b.num[yy]++;
  79. }
  80. else{
  81. break;
  82. }
  83. }
  84. yy++;
  85. xx += 2;
  86. }
  87. for (int i = b.start; i <= b.end; i++){
  88. cout << b.num[i];
  89. }
  90. cout << endl;
  91. return 0;
  92. }
  93. bool comp(NEW_NUM a, int a_start,int a_end, NEW_NUM b, int b_start,int b_end){
  94. int i = a_start;
  95. while (a.num[i] == 0)i++;
  96. a.start = i;
  97. int j = b_start;
  98. while (b.num[j] == 0)j++;
  99. if (a_end - i>b_end - j)return true;
  100. if (a_end - i < b_end - j) return false;
  101. int k = 0;
  102. while (k <= a_end - i){
  103. if (a.num[k + i] > b.num[k + j]) return true;
  104. if (a.num[k + i] < b.num[k + j]) return false;
  105. k++;
  106. }
  107. return true;
  108. }
  109. void sub(NEW_NUM &a, int a_start, int a_end, NEW_NUM b, int b_start, int b_end){
  110. int i = a_end;
  111. int j = b_end;
  112. while (j >= b_start){
  113. if (a.num[i] >= b.num[j]) a.num[i] -= b.num[j];
  114. else{
  115. a.num[i - 1]--;
  116. a.num[i] = 10 + a.num[i] - b.num[j];
  117. }
  118. i--;
  119. j--;
  120. }
  121. }
  122. void add(NEW_NUM &temp, int num){
  123. temp.num[temp.end] += num;
  124. int i = temp.end;
  125. while (temp.num[i]>=10){
  126. temp.num[i - 1] += temp.num[i] / 10;
  127. temp.num[i] = temp.num[i] % 10;
  128. i--;
  129. }
  130. if (i < temp.start) temp.start = i;
  131. }
  132. void calculate(NEW_NUM &temp){ //temp*2*i
  133. for (int i = temp.end; i >= temp.start; i--){
  134. temp.num[i] = 2 * temp.num[i];
  135. }
  136. for (int i = temp.end; i >= temp.start; i--){
  137. if (temp.num[i] >= 10){
  138. temp.num[i - 1] += temp.num[i] / 10;
  139. temp.num[i] = temp.num[i] % 10;
  140. }
  141. }
  142. int i = 0;
  143. while (temp.num[i] == 0){
  144. i++;
  145. }
  146. temp.start = i;
  147. }

*高精度取模

【代码实现】
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int a[9999],b[9999],c[9999],ans[9999];
  4. void read(int *x) {
  5. string s;
  6. cin>>s;
  7. x[0]=s.length();
  8. for(int i=1; i<=x[0]; i++)
  9. x[i]=s[i-1]-48;
  10. reverse(x+1,x+x[0]+1);
  11. }
  12. void print(int *x) {
  13. for(int i=x[0]; i>=1; i--)
  14. cout<<x[i];
  15. cout<<endl;
  16. }
  17. bool pd() {
  18. if(c[0]>b[0])return 1;
  19. if(c[0]<b[0])return 0;
  20. for(int i=c[0]; i>=1; i--) {
  21. if(c[i]>b[i])return 1;
  22. if(c[i]<b[i])return 0;
  23. }
  24. return 1;
  25. }
  26. void js() {
  27. int d=0;
  28. for(int i=1; i<=c[0]; i++) {
  29. c[i]-=b[i]+d;
  30. if(c[i]<0) {
  31. c[i]+=10;
  32. d=1;
  33. } else d=0;
  34. }
  35. while(c[0]>1&&!c[c[0]])c[0]--;
  36. }
  37. int main() {
  38. read(a);
  39. read(b);
  40. c[0]=0;
  41. for(int i=a[0]; i>=1; i--) {
  42. for(int j=c[0]; j>=1; j--)c[j+1]=c[j];
  43. c[1]=a[i];
  44. c[0]++;
  45. // print(c);
  46. while(pd()) {
  47. js();
  48. ans[i]++;
  49. }
  50. }
  51. ans[0]=a[0];
  52. while(ans[0]>1&&!ans[ans[0]])ans[0]--;
  53. print(c);
  54. return 0;
  55. }

*高精度求小数幂次

【代码实现】
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1300; //100000^250有1250位
  4. int pt;
  5. struct Bign{
  6. int s[MAXN], len;
  7. Bign(int num = 0){
  8. memset(s, 0, sizeof(s));
  9. len = 1;
  10. s[1] = num;
  11. }
  12. Bign operator * (int b)const{
  13. Bign c;
  14. c.len = len + 10;
  15. for (int i = 1; i <= c.len; i++){
  16. c.s[i] += s[i] * b;
  17. c.s[i+1] = c.s[i] / 10;
  18. c.s[i] %= 10;
  19. }
  20. c.clean();
  21. return c;
  22. }
  23. void clean(){
  24. while (len > 1 && !s[len]) len--;
  25. }
  26. };
  27. ostream& operator << (ostream &out, const Bign &x){
  28. int i;
  29. for (i = x.len; i > 0 && i > pt; i--) //输出整数部分
  30. out << x.s[i];
  31. if (i){ //若i不为0,表示还有小数部分
  32. out << "."; //先输出"."
  33. for (i = pt; i > 0; i--) //再输出小数部分
  34. out << x.s[i];
  35. }
  36. return out;
  37. }
  38. int main(){
  39. double a;
  40. int n;
  41. while (cin >> a >> n){
  42. //求a的小数位数
  43. pt = 0; //pt记录a的小数位数
  44. while (fabs(a - round(a)) > 1e-6){ //若a不等于a的整数部分,表示a不是整数
  45. pt++; //小数位数加一位
  46. a *= 10;
  47. }
  48. pt *= n; //a^n的小数位数等于a的小数位数 ×n
  49. //求s = a ^ n
  50. Bign s = 1;
  51. for (int i = 1; i <= n; i++)
  52. s = s * (int)round(a);
  53. cout << s << endl;
  54. }
  55. return 0;
  56. }

*Trie树

【简介】

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

【代码实现】

  1. #include<cstring>
  2. #include<iostream>
  3. #include<conio.h>
  4. using namespace std;
  5. namespace trie
  6. {
  7. template<classT,size_t CHILD_MAX>
  8. /*
  9. ParameterT:Typeofreserveddata.
  10. ParameterCHILD_MAX:Sizeofarryofpointerstochildnode.
  11. */
  12. struct nod
  13. {
  14. Treserved;
  15. nod<T,CHILD_MAX>*child[CHILD_MAX];
  16. nod()
  17. {
  18. memset(this,0,sizeof*this);
  19. }
  20. ~nod()
  21. {
  22. for(unsignedi=0; i<CHILD_MAX; i++)
  23. deletechild[i];
  24. }
  25. void Traversal(char*str,unsignedindex)
  26. {
  27. unsignedi;
  28. for(i=0; i<index; i++)
  29. cout<<str[i];
  30. cout<<'\t'<<reserved<<endl;
  31. for(i=0; i<CHILD_MAX; i++)
  32. {
  33. if(child[i])
  34. {
  35. str[index]=i;
  36. child[i]->Traversal(str,index+1);
  37. }
  38. }
  39. return;
  40. }
  41. };
  42. template<classT,size_t CHILD_MAX=127>
  43. /*
  44. ParameterT:Typeofreserveddata.
  45. ParameterCHILD_MAX:Sizeofarryofpointerstochildnode.
  46. */
  47. classtrie
  48. {
  49. private:
  50. nod<T,CHILD_MAX>root;
  51. public:
  52. nod<T,CHILD_MAX>*AddStr(char*str);
  53. nod<T,CHILD_MAX>*FindStr(char*str);
  54. boolDeleteStr(char*str);
  55. void Traversal()
  56. {
  57. char str[100];
  58. root.Traversal(str,0);
  59. }
  60. };
  61. template<classT,size_tCHILD_MAX>
  62. nod<T,CHILD_MAX>*trie<T,CHILD_MAX>::AddStr(char*str)
  63. {
  64. nod<T,CHILD_MAX>*now=&root;
  65. do
  66. {
  67. if(now->child[*str]==NULL)
  68. now->child[*str]=newnod<T,CHILD_MAX>;
  69. now=now->child[*str];
  70. }
  71. while(*(++str)!='\0');
  72. return now;
  73. }
  74. template<classT,size_tCHILD_MAX>
  75. nod<T,CHILD_MAX>*trie<T,CHILD_MAX>::FindStr(char*str)
  76. {
  77. nod<T,CHILD_MAX>*now=&root;
  78. do
  79. {
  80. if(now->child[*str]==NULL)
  81. return NULL;
  82. now=now->child[*str];
  83. }
  84. while(*(++str)!='\0');
  85. returnnow;
  86. }
  87. template<classT,size_tCHILD_MAX>
  88. bool trie<T,CHILD_MAX>::DeleteStr(char*str)
  89. {
  90. nod<T,CHILD_MAX>**nods=new nod<T,CHILD_MAX>*[strlen(str)];
  91. intsnods=1;
  92. nod<T,CHILD_MAX>*now=&root;
  93. nods[0]=&root;
  94. do
  95. {
  96. if(now->child[*str]==NULL)
  97. returnfalse;
  98. nods[snods++]=now=now->child[*str];
  99. }
  100. while(*(++str)!='\0');
  101. snods--;
  102. while(snods>0)
  103. {
  104. for(unsigned i=0; i<CHILD_MAX; i++)
  105. if(nods[snods]->child[i]!=NULL)
  106. return true;
  107. delete nods[snods];
  108. nods[--snods]->child[*(--str)]=NULL;
  109. }
  110. return true;
  111. }
  112. }
  113. int main()
  114. {
  115. //TestProgram
  116. trie::trie<int>tree;
  117. while(1)
  118. {
  119. cout<<"1foraddastring"<<endl;
  120. cout<<"2forfindastring"<<endl;
  121. cout<<"3fordeleteastring"<<endl;
  122. cout<<"4fortraversal"<<endl;
  123. cout<<"5forexit"<<endl;
  124. charstr[100];
  125. switch(getch())
  126. {
  127. case '1':
  128. cin.getline(str,100);
  129. cout<<"Thisstinghasexistedfor"<<tree.AddStr(str)->reserved++<<"times."<<endl;
  130. break;
  131. case '2':
  132. cin.getline(str,100);
  133. trie::nod<int,127>*find;
  134. find=tree.FindStr(str);
  135. if(!find)
  136. cout<<"Nofound."<<endl;
  137. else
  138. cout<<"Thisstinghasexistedfor"<<find->reserved<<"times."<<endl;
  139. break;
  140. case '3':
  141. cin.getline(str,100);
  142. cout<<"Theactionis"<<(tree.DeleteStr(str)?"Successful.":"Unsuccessful.")<<endl;
  143. break;
  144. case '4':
  145. tree.Traversal();
  146. break;
  147. case '5':
  148. return 0;
  149. }
  150. }
  151. return 0;
  152. }

*后缀数组

【简介】

在字符串处理当中,后缀树和后缀数组都是非常有力的工具。其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3809
  2. #include<bits/stdc++.h>
  3. #define N 1000010
  4. using namespace std;
  5. int t1[N],t2[N],sa[N],h[N],rk[N],c[10*N],a[N];
  6. char s[N];
  7. int m,n;
  8. void calcsa(int n,int m){
  9. int *x=t1,*y=t2,p=0,f=0;
  10. for(int i=1;i<=m;i++)c[i]=0;
  11. for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
  12. for(int i=1;i<=m;i++)c[i]+=c[i-1];
  13. for(int i=n;i;i--)sa[c[x[i]]--]=i;
  14. for(int i=1;i<=n&&p<=n;i<<=1){
  15. p=0;
  16. for(int j=n-i+1;j<=n;j++)y[++p]=j;
  17. for(int j=1;j<=n;j++)if(sa[j]>i)y[++p]=sa[j]-i;
  18. for(int j=1;j<=m;j++)c[j]=0;
  19. for(int j=1;j<=n;j++)c[x[y[j]]]++;
  20. for(int i=1;i<=m;i++)c[i]+=c[i-1];
  21. for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
  22. swap(x,y);x[sa[1]]=1;p=2;
  23. for(int j=2;j<=n;j++)
  24. x[sa[j]]=y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i]?p-1:p++;
  25. m=p;
  26. }
  27. for(int i=1;i<=n;i++)rk[sa[i]]=i;
  28. for(int i=1;i<=n;i++){
  29. int j=sa[rk[i]-1];
  30. if(f)f--;while(a[i+f]==a[j+f])f++;
  31. h[rk[i]]=f;
  32. }
  33. }
  34. int main(){
  35. scanf("%s",s);int len=strlen(s);
  36. for(int i=0;i<len;i++)a[++n]=s[i]-' ';
  37. calcsa(n,10000);
  38. for(int i=1;i<=n;i++)printf("%d ",sa[i]);
  39. return 0;
  40. }

*后缀自动机

【代码实现】

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #define ll long long
  6. using namespace std;
  7. const int N=2010000;
  8. char s[N];
  9. int fa[N],ch[N][26],len[N],siz[N];
  10. int lst=1,node=1,l,t[N],A[N];
  11. ll ans;
  12. void Extend(int c)
  13. {
  14. /*
  15. 2+2+2+3行,那么多while但是复杂度是O(n)
  16. */
  17. int f=lst,p=++node;lst=p;
  18. len[p]=len[f]+1;siz[p]=1;
  19. /*
  20. f为以c结尾的前缀的倒数第二个节点,p为倒数第一个(新建)
  21. len[i] 表示i节点的longest,不用记录shortest(概念在hihocoder后缀自动机1上讲得十分详细)
  22. siz[i]表示以i所代表的endpos的集合元素大小,即所对应的字符串集出现的次数
  23. 不用担心复制后的siz,在parent树上复制后的点的siz是它所有儿子siz之和,比1多
  24. */
  25. while(f&&!ch[f][c]) ch[f][c]=p,f=fa[f];
  26. if(!f) {fa[p]=1;return;}
  27. /*
  28. 把前面的一段没有c儿子的节点的c儿子指向p
  29. Situation 1 如果跳到最前面的根的时候,那么把p的parent树上的父亲置为1
  30. */
  31. int x=ch[f][c],y=++node;
  32. if(len[f]+1==len[x]) {fa[p]=x;node--;return;}
  33. /*
  34. x表示从p一直跳parent树得到的第一个有c儿子的节点的c儿子
  35. Situation 2 如果节点x表示的endpos所对应的字符串集合只有一个字符串,那么把p的parent树父亲设置为x
  36. Situation 2 和 Situation 3 本质不同!!!与机房dalao们讨论两天才知道Situation 2 必须特判的原因!!!详见上方链接的博客
  37. */
  38. len[y]=len[f]+1; fa[y]=fa[x]; fa[x]=fa[p]=y;
  39. memcpy(ch[y],ch[x],sizeof(ch[y]));
  40. while(f&&ch[f][c]==x) ch[f][c]=y,f=fa[f];
  41. /*
  42. Situation 3 否则把x点复制一遍(parent树父亲、儿子),同时len要更新
  43. (注意len[x]!=len[f]+1,因为通过加点会使x父亲改变)
  44. 然后把x点和p点的父亲指向复制点y,再将前面所有本连x的点连向y
  45. */
  46. }
  47. int main()
  48. {
  49. //Part 1 建立后缀自动机
  50. scanf("%s",s); l=strlen(s);
  51. for(int i=l;i>=1;i--) s[i]=s[i-1];s[0]=0;
  52. for(int i=1;i<=l;i++) Extend(s[i]-'a');
  53. //Part 2 按len从大到小排序(和SA好像啊)后计算答案
  54. for(int i=1;i<=node;i++) t[len[i]]++;
  55. for(int i=1;i<=node;i++) t[i]+=t[i-1];
  56. for(int i=1;i<=node;i++) A[t[len[i]]--]=i;
  57. for(int i=node;i>=1;i--)
  58. {//从小到大枚举,实际上在模拟parent树的DFS
  59. int now=A[i];siz[fa[now]]+=siz[now];
  60. if(siz[now]>1) ans=max(ans,1ll*siz[now]*len[now]);
  61. }
  62. printf("%lld\n",ans);
  63. return 0;
  64. }

*AC自动机

【简介】

在计算机科学中,Aho–Corasick算法是由Alfred V. Aho和Margaret J.Corasick 发明的字符串搜索算法,用于在输入的一串字符串中匹配有限组“字典”中的子串 。它与普通字符串匹配的不同点在于同时与所有字典串进行匹配。算法均摊情况下具有近似于线性的时间复杂度,约为字符串的长度加所有匹配的数量。然而由于需要找到所有匹配数,如果每个子串互相匹配(如字典为a,aa,aaa,aaaa,输入的字符串为aaaa),算法的时间复杂度会近似于匹配的二次函数。

【代码实现】

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<queue>
  7. #include<algorithm>
  8. using namespace std;
  9. struct Tree//字典树
  10. {
  11. int fail;//失配指针
  12. int vis[26];//子节点的位置
  13. int end;//标记以这个节点结尾的单词编号
  14. }AC[100000];//Trie树
  15. int cnt=0;//Trie的指针
  16. struct Result
  17. {
  18. int num;
  19. int pos;
  20. }Ans[100000];//所有单词的出现次数
  21. bool operator <(Result a,Result b)
  22. {
  23. if(a.num!=b.num)
  24. return a.num>b.num;
  25. else
  26. return a.pos<b.pos;
  27. }
  28. string s[100000];
  29. inline void Clean(int x)
  30. {
  31. memset(AC[x].vis,0,sizeof(AC[x].vis));
  32. AC[x].fail=0;
  33. AC[x].end=0;
  34. }
  35. inline void Build(string s,int Num)
  36. {
  37. int l=s.length();
  38. int now=0;//字典树的当前指针
  39. for(int i=0;i<l;++i)//构造Trie树
  40. {
  41. if(AC[now].vis[s[i]-'a']==0)//Trie树没有这个子节点
  42. {
  43. AC[now].vis[s[i]-'a']=++cnt;//构造出来
  44. Clean(cnt);
  45. }
  46. now=AC[now].vis[s[i]-'a'];//向下构造
  47. }
  48. AC[now].end=Num;//标记单词结尾
  49. }
  50. void Get_fail()//构造fail指针
  51. {
  52. queue<int> Q;//队列
  53. for(int i=0;i<26;++i)//第二层的fail指针提前处理一下
  54. {
  55. if(AC[0].vis[i]!=0)
  56. {
  57. AC[AC[0].vis[i]].fail=0;//指向根节点
  58. Q.push(AC[0].vis[i]);//压入队列
  59. }
  60. }
  61. while(!Q.empty())//BFS求fail指针
  62. {
  63. int u=Q.front();
  64. Q.pop();
  65. for(int i=0;i<26;++i)//枚举所有子节点
  66. {
  67. if(AC[u].vis[i]!=0)//存在这个子节点
  68. {
  69. AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
  70. //子节点的fail指针指向当前节点的
  71. //fail指针所指向的节点的相同子节点
  72. Q.push(AC[u].vis[i]);//压入队列
  73. }
  74. else//不存在这个子节点
  75. AC[u].vis[i]=AC[AC[u].fail].vis[i];
  76. //当前节点的这个子节点指向当
  77. //前节点fail指针的这个子节点
  78. }
  79. }
  80. }
  81. int AC_Query(string s)//AC自动机匹配
  82. {
  83. int l=s.length();
  84. int now=0,ans=0;
  85. for(int i=0;i<l;++i)
  86. {
  87. now=AC[now].vis[s[i]-'a'];//向下一层
  88. for(int t=now;t;t=AC[t].fail)//循环求解
  89. Ans[AC[t].end].num++;
  90. }
  91. return ans;
  92. }
  93. int main()
  94. {
  95. int n;
  96. while(233)
  97. {
  98. cin>>n;
  99. if(n==0)break;
  100. cnt=0;
  101. Clean(0);
  102. for(int i=1;i<=n;++i)
  103. {
  104. cin>>s[i];
  105. Ans[i].num=0;
  106. Ans[i].pos=i;
  107. Build(s[i],i);
  108. }
  109. AC[0].fail=0;//结束标志
  110. Get_fail();//求出失配指针
  111. cin>>s[0];//文本串
  112. AC_Query(s[0]);
  113. sort(&Ans[1],&Ans[n+1]);
  114. cout<<Ans[1].num<<endl;
  115. cout<<s[Ans[1].pos]<<endl;
  116. for(int i=2;i<=n;++i)
  117. {
  118. if(Ans[i].num==Ans[i-1].num)
  119. cout<<s[Ans[i].pos]<<endl;
  120. else
  121. break;
  122. }
  123. }
  124. return 0;
  125. }

其他

矩阵快速幂加速递推

【代码实现】

  1. #include <iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. #include<string>
  6. #include<map>
  7. const int maxn=1000000;
  8. const int mod=10000;
  9. #define me(a) memset(a,0,sizeof(a))
  10. #define ll long long
  11. using namespace std;
  12. struct node{
  13. int a[2][2];
  14. node(){memset(a,0,sizeof(a));}
  15. };
  16. node jx(node a,node b)
  17. {
  18. node s;
  19. for(int i=0;i<2;i++)
  20. for(int j=0;j<2;j++)
  21. for(int k=0;k<2;k++)
  22. s.a[i][k]=(s.a[i][k]+a.a[i][j]*b.a[j][k])%mod;
  23. return s;
  24. }
  25. node qsort(node a,int n)
  26. {
  27. node s;
  28. for(int i=0;i<2;i++) s.a[i][i]=1;
  29. while(n)
  30. {
  31. if(n&1) s=jx(s,a);
  32. a=jx(a,a);
  33. n>>=1;
  34. }
  35. return s;
  36. }
  37. int main()
  38. {
  39. node a;
  40. int n;
  41. a.a[0][0]=a.a[1][0]=a.a[0][1]=1;
  42. while(cin>>n&&n!=-1)
  43. {
  44. node s=qsort(a,n);
  45. cout<<s.a[1][0]<<endl;
  46. }
  47. return 0;
  48. }

复杂度,常数略大

二分法

二分法求函数零点

【代码实现】
  1. //problemID:http://noi.openjudge.cn/ch0111/02/
  2. #include <iostream>
  3. #include <iomanip>
  4. #include <cmath>
  5. using namespace std;
  6. bool check(double mid)
  7. {
  8. double f = pow(mid,5)-15.0*pow(mid,4)+85.0*pow(mid,3)-225.0*pow(mid,2)+274.0*mid-121.0;
  9. if(f > 0.0) return 1;
  10. return 0;
  11. }
  12. int main()
  13. {
  14. double l=1.5, r=2.4, mid;
  15. while(r-l > 0.00000001){
  16. mid = (l+r)/2.0;
  17. if (check(mid)) l = mid;
  18. else r = mid;
  19. }
  20. cout << fixed << setprecision(6) << mid << endl;
  21. return 0;
  22. }

二分查找

【代码实现】
  1. //problemID:http://noi.openjudge.cn/ch0111/01/
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<iostream>
  5. using namespace std;
  6. #define N 100005
  7. int n,m,x,l,r,mid;
  8. long long a[N];
  9. int main(){
  10. scanf("%d",&n);
  11. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  12. scanf("%d",&m);
  13. while(m--){
  14. scanf("%d",&x);
  15. l=0,r=n+1;
  16. if(x<=a[1]) {printf("%d\n",a[1]);continue;}
  17. if(x>=a[n]) {printf("%d\n",a[n]);continue;}
  18. while(l<r){
  19. mid=(l+r)>>1;
  20. if(a[mid]<x)
  21. l=mid;
  22. else if(a[mid]>x)
  23. r=mid;
  24. else {l=mid;break;}
  25. if(l==r-1&&a[l]<x&&a[r]>x) break;
  26. }
  27. if(abs(a[l]-x)<=abs(a[r]-x))
  28. printf("%d\n",a[l]);
  29. else printf("%d\n",a[r]);
  30. }
  31. return 0;
  32. }

三分法

【代码实现】

  1. //problemID:https://www.luogu.org/problemnew/show/P3382
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. const int N = 20 + 5;
  5. #define double long double
  6. double T = 0.92, delta = 0.99, tt = 1e-14;
  7. double a[N], l, r, eps = 1e-4, ans, num;
  8. int n;
  9. void input(){
  10. scanf("%d%llf%llf", &n, &l, &r);
  11. for(int i = 0; i <= n; i++)
  12. scanf("%llf", &a[i]);
  13. return ;
  14. }
  15. double get_ans(double x){
  16. double ans = 0, now = 1;
  17. for(int i = n; i >= 0; i--){
  18. ans = ans + a[i] * now;
  19. now = now * x;
  20. }
  21. return ans;
  22. }
  23. void SA(){
  24. double now = (l + r) / 2;
  25. double t = T;
  26. while(t > tt){
  27. double temp = now + (rand() * 2 - RAND_MAX) * t;//随机一个点,保证其为正负数—//(rand()*2 - RAND_MAX)
  28. if(temp < l) temp = l;
  29. if(temp > r) temp = r;
  30. double new_ans = get_ans(temp);
  31. double DE = new_ans - ans;
  32. if(DE > 0){//如果新答案更优
  33. ans = new_ans;
  34. num = temp;
  35. now = temp;
  36. }
  37. t *= delta;
  38. }
  39. }
  40. void solve(){
  41. ans = -999999999;//y一开始ans应该特别小
  42. int qwq = 12;//跑12次
  43. while(qwq--){
  44. SA();
  45. }
  46. double x = num;
  47. printf("%.5llf\n", num);
  48. }
  49. signed main()
  50. {
  51. input();
  52. solve();
  53. return 0;
  54. }

*模拟退火

【代码实现】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. struct thing
  4. {
  5. double x,y,w;
  6. }a[1001];
  7. int n;
  8. double ansx,ansy,t,ans;
  9. #define T 0.9978
  10. double calc(double x,double y)
  11. {
  12. double res=0;
  13. for(int i=1;i<=n;i++)
  14. {
  15. res+=sqrt((a[i].x-x)*(a[i].x-x)+(a[i].y-y)*(a[i].y-y))*a[i].w;
  16. }
  17. return res;
  18. }
  19. void fire()
  20. {
  21. double xx=ansx,yy=ansy;
  22. t=2270;
  23. for(;t>=1e-14;t*=T)
  24. {
  25. double x=xx-(rand()*2-RAND_MAX)*t,y=yy-(rand()*2-RAND_MAX)*t;
  26. double tmp=calc(x,y),d=tmp-ans;
  27. if(d<0)
  28. {
  29. ans=tmp;
  30. ansx=x,ansy=y,xx=x,yy=y;
  31. }
  32. else if( exp(-d/t)*RAND_MAX>rand())
  33. xx=x,yy=y;
  34. }
  35. }
  36. int main()
  37. {
  38. srand(20011115);
  39. scanf("%d",&n);
  40. for(int i=1;i<=n;i++)
  41. {
  42. scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].w);
  43. ansx+=a[i].x,ansy+=a[i].y;
  44. }
  45. ansx/=(double)n,ansy/=(double)n;
  46. ans=calc(ansx,ansy);
  47. for(int i=1;i<=8;i++)
  48. fire();
  49. printf("%.3lf %.3lf",ansx,ansy);
  50. }

大模拟

【代码实现】(例题:杀蚂蚁)

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cmath>
  6. using namespace std;
  7. #define N 200005
  8. double eps=1e-9;
  9. int dcmp(double x)
  10. {
  11. if (x<=eps&&x>=-eps) return 0;
  12. return (x>0)?1:-1;
  13. }
  14. struct Vector
  15. {
  16. double x,y;
  17. Vector(double X=0,double Y=0)
  18. {
  19. x=X,y=Y;
  20. }
  21. };
  22. typedef Vector Point;
  23. Vector operator + (Vector a,Vector b)
  24. {return Vector(a.x+b.x,a.y+b.y);}
  25. Vector operator - (Vector a,Vector b)
  26. {return Vector(a.x-b.x,a.y-b.y);}
  27. bool operator == (Vector a,Vector b)
  28. {return a.x==b.x&&a.y==b.y;}
  29. double Dot(Vector a,Vector b)
  30. {
  31. return a.x*b.x+a.y*b.y;
  32. }
  33. double Cross(Vector a,Vector b)
  34. {
  35. return a.x*b.y-a.y*b.x;
  36. }
  37. double Len(Vector a)
  38. {
  39. return sqrt(a.x*a.x+a.y*a.y);
  40. }
  41. double DisTS(Point P,Point A,Point B)
  42. {
  43. if (A==B) return Len(P-A);
  44. Vector v=B-A,w=P-A,u=P-B;
  45. if (dcmp(Dot(v,w))<0) return Len(w);
  46. else if (dcmp(Dot(v,u))>0) return Len(u);
  47. else return fabs(Cross(v,w)/Len(v));
  48. }
  49. int n,m,guncnt,d,r,t,T;
  50. struct ANT
  51. {
  52. int x,y,lastx,lasty;
  53. int age;
  54. int level;
  55. int startblood,blood;
  56. bool carry;
  57. }ant[10];
  58. struct GUN
  59. {
  60. int x,y;
  61. }gun[25];
  62. int antcnt,tot,cake;
  63. int mp[10][10];
  64. int infor[10][10];
  65. double mi[N];
  66. int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
  67. bool flag;
  68. void AntBirth()
  69. {
  70. if (antcnt<6&&!mp[0][0])
  71. {
  72. ++antcnt;++tot;
  73. ant[antcnt].x=ant[antcnt].y=
  74. ant[antcnt].lastx=ant[antcnt].lasty=0;
  75. ant[antcnt].age=1;
  76. ant[antcnt].level=(tot-1)/6+1;
  77. ant[antcnt].startblood=
  78. ant[antcnt].blood=floor(4*mi[ant[antcnt].level]);
  79. ant[antcnt].carry=0;
  80. ++mp[0][0];
  81. }
  82. }
  83. void Information()
  84. {
  85. for (int i=1;i<=antcnt;++i)
  86. {
  87. if (ant[i].carry) infor[ant[i].x][ant[i].y]+=5;
  88. else infor[ant[i].x][ant[i].y]+=2;
  89. }
  90. }
  91. void Move()
  92. {
  93. for (int i=1;i<=antcnt;++i)
  94. {
  95. int x=ant[i].x,y=ant[i].y;
  96. int lastx=ant[i].lastx,lasty=ant[i].lasty;
  97. bool can[4];memset(can,0,sizeof(can));
  98. int Max=0,cancnt=0;
  99. for (int j=0;j<4;++j)
  100. {
  101. int nxtx=x+dx[j],nxty=y+dy[j];
  102. if (nxtx>=0&&nxtx<=n&&nxty>=0&&nxty<=m
  103. &&mp[nxtx][nxty]==0&&(nxtx!=lastx||nxty!=lasty))
  104. {
  105. can[j]=1;++cancnt;
  106. Max=max(Max,infor[nxtx][nxty]);
  107. }
  108. }
  109. if (!cancnt)
  110. {
  111. ant[i].lastx=x;ant[i].lasty=y;
  112. continue;
  113. }
  114. int dir=0;
  115. for (int j=3;j>=0;--j)
  116. {
  117. int nxtx=x+dx[j],nxty=y+dy[j];
  118. if (can[j]&&infor[nxtx][nxty]==Max)
  119. {
  120. dir=j;
  121. if (ant[i].age%5==0) break;
  122. --mp[x][y];++mp[nxtx][nxty];
  123. ant[i].lastx=x,ant[i].lasty=y;
  124. ant[i].x=nxtx,ant[i].y=nxty;
  125. break;
  126. }
  127. }
  128. if (ant[i].age%5==0)
  129. {
  130. for (int j=0;j<4;++j)
  131. {
  132. ++dir;if (dir==4) dir=0;
  133. if (can[dir])
  134. {
  135. int nxtx=x+dx[dir],nxty=y+dy[dir];
  136. --mp[x][y];++mp[nxtx][nxty];
  137. ant[i].lastx=x,ant[i].lasty=y;
  138. ant[i].x=nxtx,ant[i].y=nxty;
  139. break;
  140. }
  141. }
  142. }
  143. }
  144. }
  145. void CarryCake()
  146. {
  147. if (cake) return;
  148. for (int i=1;i<=antcnt;++i)
  149. if (ant[i].x==n&&ant[i].y==m)
  150. {
  151. ant[i].carry=1;cake=i;
  152. ant[i].blood+=ant[i].startblood/2;
  153. ant[i].blood=min(ant[i].blood,ant[i].startblood);
  154. break;
  155. }
  156. }
  157. int qr(int x)
  158. {
  159. return x*x;
  160. }
  161. int length(ANT a,GUN b)
  162. {
  163. return qr(a.x-b.x)+qr(a.y-b.y);
  164. }
  165. bool canhit(ANT a,GUN b,ANT c)
  166. {
  167. Point A=Point(b.x,b.y);
  168. Point B=Point(c.x,c.y);
  169. Point P=Point(a.x,a.y);
  170. double dis=DisTS(P,A,B);
  171. if (dcmp(dis-0.5)<=0) return 1;
  172. else return 0;
  173. }
  174. void Attack()
  175. {
  176. for (int i=1;i<=guncnt;++i)
  177. {
  178. int goal=0;
  179. if (cake&&length(ant[cake],gun[i])<=r*r) goal=cake;
  180. else
  181. {
  182. int Min=100000;
  183. for (int j=1;j<=antcnt;++j)
  184. {
  185. int now=length(ant[j],gun[i]);
  186. if (now<=r*r&&now<Min)
  187. {
  188. Min=now;
  189. goal=j;
  190. }
  191. }
  192. }
  193. if (!goal) continue;
  194. for (int j=1;j<=antcnt;++j)
  195. if (canhit(ant[j],gun[i],ant[goal])) ant[j].blood-=d;
  196. }
  197. }
  198. void CheckDeath()
  199. {
  200. int i=1;
  201. while (i<=antcnt)
  202. {
  203. if (ant[i].blood<0)
  204. {
  205. --mp[ant[i].x][ant[i].y];
  206. for (int j=i;j<antcnt;++j)
  207. ant[j]=ant[j+1];
  208. --antcnt;
  209. }
  210. else ++i;
  211. }
  212. cake=0;
  213. for (int i=1;i<=antcnt;++i)
  214. if (ant[i].carry) {cake=i;break;}
  215. }
  216. bool CheckGame()
  217. {
  218. if (!cake) return 0;
  219. for (int i=1;i<=antcnt;++i)
  220. if (!ant[i].x&&!ant[i].y&&ant[i].carry) return 1;
  221. return 0;
  222. }
  223. int main()
  224. {
  225. scanf("%d%d",&n,&m);
  226. scanf("%d%d%d",&guncnt,&d,&r);
  227. for (int i=1;i<=guncnt;++i)
  228. {
  229. scanf("%d%d",&gun[i].x,&gun[i].y);
  230. mp[gun[i].x][gun[i].y]=-1;
  231. }
  232. scanf("%d",&t);
  233. mi[0]=1.0;for (int i=1;i<=t;++i) mi[i]=mi[i-1]*1.1;
  234. for (T=1;T<=t;++T)
  235. {
  236. AntBirth();
  237. Information();
  238. Move();
  239. CarryCake();
  240. Attack();
  241. CheckDeath();
  242. flag=CheckGame();
  243. if (flag) break;
  244. for (int i=0;i<=n;++i)
  245. for (int j=0;j<=m;++j)
  246. if (infor[i][j]) --infor[i][j];
  247. for (int i=1;i<=antcnt;++i)
  248. ++ant[i].age;
  249. }
  250. if (flag) printf("Game over after %d seconds\n",T);
  251. else puts("The game is going on");
  252. printf("%d\n",antcnt);
  253. for (int i=1;i<=antcnt;++i)
  254. printf("%d %d %d %d %d\n",ant[i].age-1,ant[i].level,ant[i].blood,ant[i].x,ant[i].y);
  255. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注