《算法竞赛进阶指南》第一章-基础算法
位运算⋙
89.a^b(快速幂)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=200000+7;
  ll ksm(ll n,ll x,ll p) {     ll ans=1;     ans=ans%p;     while(x)     {         if(x&1) ans=ans*n%p;         n=n*n%p;         x>>=1;     }     return ans; } int main() {     ll a,b,p;     cin>>a>>b>>p;     cout<<ksm(a,b,p)<<endl;          return 0; }
 
  | 
 
90.64位整数乘法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=200000+7;
  ll multi_big(ll a,ll b,ll p) {     ll ans=0;     while(b)     {         if(b&1) ans=(ans+a)%p;         a=a*2%p;         b>>=1;     }     return ans; } int main() {     ll a,b,p;     cin>>a>>b>>p;     cout<<multi_big(a,b,p)<<endl;          return 0; }
 
  | 
 
递推与递归⋙
※91.最短Hamilton路径(位运算,状压DP)
题目链接:https://www.acwing.com/problem/content/93/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=10+7; const double pi=cos(-1);
  int dp[(1<<20)+2][20]; int mp[20+3][20+3]; int hamilton(int n) {     memset(dp,0x3f,sizeof dp);     dp[1][0]=0;     for(int i=1;i<(1<<n);i++)     {         for(int j=0;j<n;j++)         {             if((i>>j)&1)             {                 for(int k=0;k<n;k++)                 {                     if((i^(1<<j))>>k&1)                     {                         dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+mp[k][j]);                     }                 }             }         }     }     return dp[(1<<n)-1][n-1]; }
 
 
  int main() {     int n;     cin>>n;     for(int i=0;i<n;i++)         for(int j=0;j<n;j++)             cin>>mp[i][j];     cout<<hamilton(n)<<endl;     return 0; }
 
  | 
 
92. 递归实现指数型枚举(二进制枚举)
题目链接:https://www.acwing.com/problem/content/94/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | #include <bits/stdc++.h> using namespace std;
  int main() {     int n;     scanf("%d",&n);     for(int i=0; i<=(1<<n)-1; i++)     {         for(int j=n; j>=1; j--)         {             if((i>>j-1)&1)                 printf("%d ",n-j+1);         }         printf("\n");     }     return 0; }
 
  | 
 
93. 递归实现组合型枚举(递归枚举)
题目链接:https://www.acwing.com/problem/content/95/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=10+7; const double pi=cos(-1);
  vector<int> chosen; int n,m; void calc(int x) {     if(chosen.size()==m)     {         for(int i=0;i<chosen.size();i++)         {             printf("%d ",chosen[i]);         }         printf("\n");         return;     }     if(x>n) return;     chosen.push_back(x);     calc(x+1);     chosen.pop_back();     calc(x+1); } int main() {     scanf("%d%d",&n,&m);     calc(1);     return 0; }
 
  | 
 
※94. 递归实现排列型枚举(递归排列枚举)
题目链接:https://www.acwing.com/problem/content/96/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=10+7; const double pi=cos(-1);
 
  int order[20]; bool chosen[20]; int n; void calc(int k) {     if(k==n+1)     {         for(int i=1; i<=n; i++)         {             printf("%d ",order[i]);         }         printf("\n");         return;     }     for(int i=1; i<=n; i++)     {         if(chosen[i])             continue;         order[k]=i;         chosen[i]=true;         calc(k+1);         chosen[i]=false;         order[k]=0;     } } int main() {     scanf("%d",&n);     calc(1);     return 0; }
 
  | 
 
95. 费解的开关(思维+递推+二进制)
题目链接:https://www.acwing.com/problem/content/description/97/
递推,枚举第一层,下面只要是0就改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=500+7; const double pi=cos(-1);
  char mp[8][8]; int m[8][8]; int T; void change(int x,int y) {     if(x+1<=5&&y>=1&&y<=5) m[x+1][y]=m[x+1][y]^1;     if(x-1>=1&&y>=1&&y<=5) m[x-1][y]=m[x-1][y]^1;     if(y-1>=1&&x>=1&&x<=5) m[x][y-1]=m[x][y-1]^1;     if(y+1<=5&&x>=1&&x<=5) m[x][y+1]=m[x][y+1]^1;     if(x>=1&&x<=5&&y>=1&&y<=5) m[x][y]=m[x][y]^1; } bool check() {     for(int i=1;i<=5;i++)         for(int j=1;j<=5;j++)             if(m[i][j]!=1) return false;     return true; } int main() {     scanf("%d",&T);     while(T--)     {         for(int i=1;i<=5;i++) scanf("%s",mp[i]+1);         int ans=INF;         for(int i=0;i<(1<<5);i++)         {             int step=0;             for(int j=1;j<=5;j++)                 for(int k=1;k<=5;k++)                     m[j][k]=mp[j][k]-'0';             for(int j=1;j<=5;j++)                 if(((i>>(j-1))&1)==0) {change(1,j);step++;}             for(int j=2;j<=5;j++)                 for(int k=1;k<=5;k++)                     if(!m[j-1][k]) {change(j,k);step++;}             if(check()==true&&step<=6) ans=min(ans,step);         }         if(ans==INF) printf("-1\n");         else printf("%d\n",ans);
      }     return 0; }
 
  | 
 
96. 奇怪的汉诺塔(DP+递推)
题目链接:https://www.acwing.com/problem/content/98/
这里的汉诺塔有四个桩子,但我们可以从三个桩子入手。
三个桩子递推很容易推出 式子$f[i]=2*f[i-1]+1$ 其中i为圆盘数
那么在考虑四个桩子的情况,我们不妨把四个桩子i个圆盘需要的操作次数设为dp[i]
考虑如何构造转移方程:
加入我们先转移$j$个 那么就还剩$n-i$个需要转移,这$n-i$个转移的时候有一个桩已经被开始的j个占据了,也就是说还剩下三个桩子,这时候用上面的$f[n-i]$就是三个桩子的情况下转移到另一处的耗费。
最后我们需要把转移的两个桩子合并在一起,需要$dp[j]$
转移方程为$dp[i]=min(dp[i],dp[j]+dp[j]+f[i-j])$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=500+7; const double pi=cos(-1);
  int f[25],dp[25]; int main() {     f[1]=1;     for(int i=2; i<=12; i++)         f[i]=2*f[i-1]+1;     memset(dp,0x3f,sizeof dp);     dp[0]=0;     for(int i=1; i<=12; i++)         for(int j=0; j<i; j++)             dp[i]=min(dp[i],dp[j]+dp[j]+f[i-j]);     for(int i=1;i<=12;i++) printf("%d\n",dp[i]);     return 0; }
 
  | 
 
97. 约数之和(矩阵快速幂+分治/逆元+特判)
题目链接:https://www.acwing.com/problem/content/99/
算贡献就好,可以分治也可以逆元来做,但是注意一点,由于模数为9901,所以费马小定理不能直接用,只有当$gcd(p,a)=1$时才可以用(把我恶心坏了,看了一个小时找不出来错误),所以要特判解决这个问题,当这个式子$gcd(p,a)=1$成立时单独拿出来加到ans中去,像下面这样。
 | if((p[i]-1)%MOD==0) {     for(int j=1;j<=cnt[i];j++)     {         ans=(ans+ksm(p[i],j,MOD))%MOD;     } }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=5e7+7; const int MOD=9901; const double pi=cos(-1);
  ll ksm(int n,int x,int p) {     long long int ans=1;     ans=ans%p;     while(x)     {         if(x&1) ans=ans*1ll*n%p;         n=n*1ll*n%p;         x>>=1;     }     return ans; } int gcd(int a,int b) {     return b==0?a:gcd(b,a%b); } int p[MAXN],cnt[MAXN]; int main() {     int a,b;     cin>>a>>b;     int num=0;     for(int i=2;i<=a;i++)     {         while(a%i==0)         {             if(p[num]!=i) num++;             p[num]=i;             cnt[num]++;             a=a/i;         }     }     for(int i=1;i<=num;i++) cnt[i]=cnt[i]*b;     ll ans=1;     for(int i=1;i<=num;i++)     {         if((p[i]-1)%MOD==0)         {             for(int j=1;j<=cnt[i];j++)             {                 ans=(ans+ksm(p[i],j,MOD))%MOD;             }         }         else         {             ll c1=ksm(p[i],cnt[i]+1,MOD);             c1=c1+MOD;             ans=ans*(c1-1)*ksm(p[i]-1,MOD-2,MOD)%MOD;         }     }     if(a==0) printf("0");     else if(b==0) printf("1");     else printf("%lld",ans);     return 0; }
 
  | 
 
98. 分形之城(线性代数坐标变换+规律+递归+分形图)
题目链接:https://www.acwing.com/problem/content/100/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define pll pair<ll,ll> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=5e7+7; const int MOD=9901; const double pi=cos(-1);
  pll cal(ll n,ll m) {     if(n==0) return {0,0};     ll len=1ll<<n-1,cnt=1ll<<2*n-2;     auto pos=cal(n-1,m%cnt);     auto x=pos.first,y=pos.second;     auto z=m/cnt;     if(z==0) return {y,x};     if(z==1) return {x,y+len};     if(z==2) return {x+len,y+len};     else return {2*len-1-y,len-1-x}; } int main() {     int T;     cin>>T;     while(T--)     {         ll n,a,b;         cin>>n>>a>>b;         auto ac=cal(n,a-1);         auto bc=cal(n,b-1);         double x=ac.first-bc.first;         double y=ac.second-bc.second;         printf("%.0f\n",sqrt(x*x+y*y)*10);     }     return 0; }
 
  | 
 
前缀和⋙
99.激光炸弹(二位前缀和+思维)
题目链接:https://www.acwing.com/problem/content/101/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define pll pair<ll,ll> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=5000+4; const int MOD=9901; const double pi=cos(-1);
  int n,r; int max_r,max_c; int s[MAXN][MAXN]; int main() {     scanf("%d%d",&n,&r);     max_r=max_c=r;     for(int i=1;i<=n;i++)     {         int x,y,w;         scanf("%d%d%d",&x,&y,&w);         x++;         y++;         s[x][y]=w;         max_r=max(max_r,x);         max_c=max(max_c,y);     }     for(int i=1;i<=max_r;i++)     {         for(int j=1;j<=max_c;j++)         {             s[i][j]=s[i-1][j]+s[i][j-1]+s[i][j]-s[i-1][j-1];         }     }     int ans=0;     for(int i=r;i<=max_r;i++)     {         for(int j=r;j<=max_c;j++)         {             ans=max(ans,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);         }     }     printf("%d\n",ans);     return 0; }
 
  | 
 
差分⋙
100.增减序列
题目链接:https://www.acwing.com/problem/content/102/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=2e5+5; const int MOD=1e9+7; const double pi=cos(-1);
  ll p=0,q=0; int a[MAXN]; int d[MAXN]; int main() {     int n;     cin>>n;     for(int i=1;i<=n;i++) scanf("%d",&a[i]);     for(int i=2;i<=n;i++)     {         d[i]=a[i]-a[i-1];         if(d[i]>0) p+=d[i];         if(d[i]<0) q+=(-d[i]);     }     printf("%lld\n%lld",min(p,q)+abs(p-q),abs(p-q)+1);     return 0; }
 
  | 
 
101. 最高的牛
题目链接:https://www.acwing.com/problem/content/103/
题目中保证了m组对中不会出现重复的,但如果不保证的话只需要把每一对存入$pair$即可 然后在q[x]!=y来去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   |  #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> typedef long long ll; const int INF=0x3f3f3f3f; const int MAXN=1e4+5; const int MOD=1e9+7; const double pi=cos(-1);
  pii a[MAXN]; int c[MAXN],d[MAXN]; map<ll,ll>q; int main() {     int n,p,h,m;     cin>>n>>p>>h>>m;     for(int i=1;i<=m;i++)     {         int x,y;         cin>>x>>y;         if(x>y) swap(x,y);         if(q[x]!=y)         {             q[x]=y;             d[x+1]--,d[y]++;         }     }     for(int i=1;i<=n;i++) c[i]=c[i-1]+d[i];     for(int i=1;i<=n;i++) printf("%d\n",c[i]+h);     return 0; }
 
  | 
 
二分⋙