大吉大利
Solution
位运算常用的优化技巧就是按位计算。用 表示第 位为 的数的个数。由与运算的性质可以得到,两个数二进制第 位都为 时,会使答案加 。因此便可以边读入边处理,最后将答案乘 并加上数本身即可。
时间复杂度:
Code
#include <iostream> #include <cstdio> using namespace std; const int maxn=1e5+10; int n,a[maxn],sum[31],p[31]; long long ans; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; p[0]=1; for(int i=1;i<=30;i++) p[i]=p[i-1]*2; for(int i=1;i<=n;i++) for(int j=0;j<=30;j++) if((a[i]>>j)&1){ ans+=sum[j]*p[j]; sum[j]++; } ans*=2; for(int i=1;i<=n;i++) ans+=a[i]; cout<<ans; return 0; }
三角形周长和
Solution
此处的边长等于两点间的曼哈顿距离。一条边会在 个三角形中出现,按边统计答案即可。
时间复杂度:
Code
#include <iostream> #include <cstdio> #include <cmath> #define ll long long using namespace std; const int maxn=1e3+10; const int mod=998244353; int n,x[maxn],y[maxn]; ll ans; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) ans=(ans+(abs(x[i]-x[j])%mod+abs(y[i]-y[j])%mod)%mod*(n-2))%mod; cout<<ans; return 0; }
操作集锦
Solution
看到题应该是一道 题。
首先设计状态:我们可以发现两个状态量,分别是当前已处理的位数 与当前已选择的位数 。若不考虑重复,则有
即选与不选两种情况。
题目要求本质不同,所以需要减去重复的情况。考虑什么时候会重复:设 位和 位字母相同,且 。容易发现凡是 加上的情况, 都会加上。也就是说 真包含于 。所以将 减去即可。具体来说,设 表示字母 的上一个位置,则
特别地,有 。
时间复杂度:
Code
#include <iostream> #include <cstdio> #include <cmath> #define ll long long using namespace std; const int maxn=1e3+10; const int mod=1e9+7; ll n,k,f[maxn][maxn],p[26]; string s; int main(){ cin>>n>>k>>s; f[0][0]=1; for(int i=1;i<=n;i++){ f[i][0]=1; for(int j=1;j<=k;j++){ f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod; if(p[s[i-1]-'a']) f[i][j]=(f[i][j]+mod-f[p[s[i-1]-'a']-1][j-1])%mod; } p[s[i-1]-'a']=i; } cout<<f[n][k]; return 0; }
斩杀线计算大师
Solution
第一眼看到以为是一道 题,但是需要枚举其中一项,如果数据强的话会被卡掉。
对于这种给定序列 和数字 ,询问是否能被表示的题目,有一种更好地通法:同余最短路。
设 表示在模一个数(不妨设为 )的意义下余 的最小能被表示的数。如果感觉很绕,举个栗子: .那么在模 意义下,。
然后,就可以使用 算法求出 数组。这样做的原理是:若 可以被表示,则 一定可以被表示(加若干个 得到)。即 , 。若 ,则 可以被表示。由于题目要求输出方案数,所以维护一个数组 储存转移到这个状态时选择的哪个数即可。
时间复杂度:
一道很好的同余最短路练习题:Sums
Code
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #define ll long long using namespace std; const int maxn=1e5+10; ll a,b,c,k,ans[4],dis[maxn],vis[maxn],nxt[maxn]; priority_queue<pair<int,int> >q; void Dijkstra(){ memset(dis,0x7f,sizeof(dis)); dis[0]=0; q.push(make_pair(0,0)); ll u; while(!q.empty()){ u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=1; if(dis[(u+a)%a]>dis[u]+a){ dis[(u+a)%a]=dis[u]+a; nxt[(u+a)%a]=a; q.push(make_pair(-dis[(u+a)%a],(u+a)%a)); } if(dis[(u+b)%a]>dis[u]+b){ dis[(u+b)%a]=dis[u]+b; nxt[(u+b)%a]=b; q.push(make_pair(-dis[(u+b)%a],(u+b)%a)); } if(dis[(u+c)%a]>dis[u]+c){ dis[(u+c)%a]=dis[u]+c; nxt[(u+c)%a]=c; q.push(make_pair(-dis[(u+c)%a],(u+c)%a)); } } } int main(){ cin>>a>>b>>c>>k; Dijkstra(); ll d=dis[k%a]; while(nxt[d%a]){ if(nxt[d%a]==a) ans[1]++; else if(nxt[d%a]==b) ans[2]++; else ans[3]++; d-=nxt[d%a]; } d=ans[1]*a+ans[2]*b+ans[3]*c; ans[1]+=(k-d)/a; cout<<ans[1]<<" "<<ans[2]<<" "<<ans[3]; return 0; }