Codeforces CodeCraft-20
小号冲黄失败...心塞塞_(:з」∠)_
\(ans=min(m,\sum_{i=1}^n a_i)\)
#include<bits/stdc++.h> using namespace std; int T,n,m,a[1111]; int main() { scanf("%d",&T); while(T--) { int s=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]),s=min(m,s+a[i]); printf("%d\n",s); } }
可以试着找找规律,以下列出\(n=4,5,6,7,8\ k=4\)时的结果
\(1234\rightarrow 4321\)
\(12345\rightarrow 45123\)
\(123456\rightarrow 456321\)
\(1234567\rightarrow 4567123\)
\(12345678\rightarrow 45678321\)
应该就能猜出结论了,枚举\(k\)即可
#include<bits/stdc++.h> using namespace std; int T,n,ans; string s,mn; string rua(int k) { if(k==n) { string res=s; reverse(res.begin(),res.end()); return res; } int t=n-k+1; if(t%2==0) { string res=s.substr(k-1,n-k+1); res+=s.substr(0,k-1); return res; } string res=s.substr(k-1,n-k+1); string tmp=s.substr(0,k-1); reverse(tmp.begin(),tmp.end()); res=res+tmp; return res; } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); cin>>s; ans=1,mn=s; for(int i=2;i<=n;i++) if(rua(i)<mn)mn=rua(i),ans=i; cout<<mn<<endl<<ans<<endl; } }
设\(\left \{ a_n \right \}\)中第一个不被\(p\)整除的数为\(a_i\),\(\left \{ b_m \right \}\)中第一个不被\(p\)整除的数为\(b_j\),则答案为\(i+j\)
证明:考虑所有满足\(0\le x < n, 0 \le y <m, x+y=i+j\)的数对\((x,y)\),显然\(x<i\)和\(y<j\)这两个不等式至少有一个成立,故\(a_x \cdot b_y\)必定为\(p\)的倍数。又由于\(p\)为质数,所以\(a_i \cdot b_j\)一定不为\(p\)的倍数,结论成立
#include<bits/stdc++.h> using namespace std; #define N 1000001 int n,m,p,a[N],b[N],l,r; int main() { scanf("%d%d%d",&n,&m,&p); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<m;i++) scanf("%d",&b[i]); while(a[l]%p==0)l++; while(b[r]%p==0)r++; printf("%d\n",l+r); }
对所有\(a[i][j]=(i,j)\)的点,设该点为'X',进行BFS,若周围有以该点为终点的点则加入队列,并将其指向上一个点
对所有\(a[i][j]=(-1,-1)\)的点,寻找一个与它相邻且同样为\((-1,-1)\)的点,若有解则一定能找到。这时可以令这两个点互相可达(造成死循环),同样把这两个点加入队列,进行类似的BFS即可
最后如果有未被赋值的点则无解
#include<bits/stdc++.h> using namespace std; #define N 1010 int n,x,y,a[N][N]; int dx[4]={0,1,0,-1}; int dy[4]={1,0,-1,0}; char ans[N][N],f[4]={'L','U','R','D'}; queue<int>q; int id(int x,int y){return (x+1)*N+y;} void rua(int x,int y) { if(ans[x][y]!='I')return; if(a[x][y]<0) for(int i=0;i<4;i++) { int nxtx=x+dx[i],nxty=y+dy[i]; if(nxtx<1 || nxtx>n || nxty<1 || nxty>n)continue; if(a[nxtx][nxty]==a[x][y]) { ans[nxtx][nxty]=f[i]; ans[x][y]=f[(i+2)%4]; q.push(id(x,y)); q.push(id(nxtx,nxty)); break; } } else ans[x][y]='X',q.push(id(x,y)); while(!q.empty()) { int cur=q.front(); q.pop(); x=cur/N-1,y=cur%N; for(int i=0;i<4;i++) { int nxtx=x+dx[i],nxty=y+dy[i]; if(nxtx<1 || nxtx>n || nxty<1 || nxty>n)continue; if(a[nxtx][nxty]==a[x][y] && ans[nxtx][nxty]=='I') ans[nxtx][nxty]=f[i],q.push(id(nxtx,nxty)); } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { scanf("%d%d",&x,&y); a[i][j]=id(x,y); ans[i][j]='I'; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]==id(i,j) || a[i][j]<0)rua(i,j); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(ans[i][j]=='I')return printf("INVALID\n"),0; printf("VALID\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) printf("%c",ans[i][j]); printf("\n"); } }
首先对所有人按\(a_i\)降序排序,进行状压DP。设\(f[i][bit]\)为前\(i\)个人,已经确定位置的状态为\(bit\)时的最优解,进行转移即可
#include<bits/stdc++.h> using namespace std; #define N 100001 #define LL long long LL n,p,k,f[N][128]; struct rua { LL v,s[8]; bool operator <(const rua &t)const{return v>t.v;} }a[N]; int main() { scanf("%lld%lld%lld",&n,&p,&k); for(LL i=1;i<=n;i++)scanf("%lld",&a[i].v); for(LL i=1;i<=n;i++) for(LL j=0;j<p;j++) scanf("%lld",&a[i].s[j]); sort(a+1,a+n+1); for(LL b=0;b<(1<<p);b++)f[0][b]=-1e18; f[0][0]=0; for(LL i=1;i<=n;i++) for(LL b=0,cnt=0;b<(1<<p);b++,cnt=0) { f[i][b]=-1e18; for(LL k=0;k<p;k++)if((1ll<<k)&b) cnt++,f[i][b]=max(f[i][b],f[i-1][(1ll<<k)^b]+a[i].s[k]); f[i][b]=max(f[i][b],f[i-1][b]+(i<=cnt+k?a[i].v:0)); } printf("%lld\n",f[n][(1<<p)-1]); }
看了几份AC代码,大概知道了其中一种做法,先讲一下大致思路等白天起床再写_(:з」∠)_
首先对\(p_i\)排序,先考虑不修改的时候答案是多少
对于任意两对数\(p_i, p_j, i<j\),若这两个数相邻,则其产生的价值为\(p_i \cdot p_j\),而能让这两个数相邻的子集数则为\(2^{n-(j-i+1)}\),其中\(n-(j-i+1)\)表示把区间\([i,j]\)掏空后的元素个数。这样我们可以得到\(2^n\cdot ans=\sum_{i=1}^n\sum_{j=i+1}^n p_i\cdot p_j \cdot 2^{n-j+i-1}\),稍微化简一下可以得到\(2\cdot ans=\sum_{i=1}^n\sum_{j=i+1}^n p_i\cdot p_j \cdot 2^{-j+i}=\sum_{i=1}^n\sum_{j=i+1}^n p_i\cdot 2^i \cdot p_j \cdot 2^{-j}\),这个时候我们就可以用线段树来求解,合并的式子为\(ans=l_{ans}+r_{ans}+l_{s1}\cdot r_{s2}\),\(s1,s2\)分别表示\(p_i\)乘上\(2^i\)和\(2^{-i}\)的和
对于有修改的情况,可以考虑离线处理
得到所有的修改的信息后,对\(p_i\)和修改的值混在一起进行排序,开一个\(n+q\)大小的线段树,并额外记录区间内有多少个位置有值。每次修改就相当于把其中一个位置变为\(0\),另外一个空位变成想要修改的值即可,这样仍然能够保证内部的值是升序排列的,把之前的式子稍作改动即可(额外考虑区间内非空位的个数带来的指数影响)
upd:过了_(:з」∠)_具体做法稍微有点偏差,详细题解随后更
#include<bits/stdc++.h> using namespace std; #define N 600001 #define MOD 1000000007 int n,m,p[N],q[N],I[N],v; struct pi { int v,id; void read(int i){scanf("%d",&v),id=i;} bool operator <(const pi &t)const{return v<t.v;} }a[N]; struct rua { int c,ans,sl,sr; }t[N<<2]; void change(int x,int i,int l,int r,int o) { if(l==r) { t[i].c=o; t[i].ans=t[i].sl=t[i].sr=0; if(o)t[i].sl=t[i].sr=1ll*I[1]*a[x].v%MOD; return; } int mid=l+r>>1,ls=i*2,rs=ls+1; if(x<=mid)change(x,ls,l,mid,o); else change(x,rs,mid+1,r,o); t[i].c=t[ls].c+t[rs].c; t[i].sl=(1ll*t[ls].sl*I[t[rs].c]+t[rs].sl)%MOD; t[i].sr=(1ll*t[rs].sr*I[t[ls].c]+t[ls].sr)%MOD; t[i].ans=(t[ls].ans+t[rs].ans+1ll*t[ls].sl*t[rs].sr)%MOD; } int main() { I[0]=1,I[1]=(MOD+1)/2; for(int i=2;i<N;i++) I[i]=1ll*I[1]*I[i-1]%MOD; scanf("%d",&n); for(int i=1;i<=n;i++) a[i].read(i),p[i]=i; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&q[i]); a[n+i].read(n+i); p[n+i]=n+i; } sort(a+1,a+n+m+1); for(int i=1;i<=n+m;i++) p[a[i].id]=i; for(int i=1;i<=n;i++) change(p[i],1,1,n+m,1); printf("%d\n",t[1].ans); for(int i=1;i<=m;i++) { change(p[q[i]],1,1,n+m,0); change(p[n+i],1,1,n+m,1),p[q[i]]=p[n+i]; printf("%d\n",t[1].ans); } }
upd2:[CodeCraft-20 (Div. 2)][Codeforces 1316F. Battalion Strength]