小号冲黄失败...心塞塞_(:з」∠)_
(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 ",s); } }
可以试着找找规律,以下列出(n=4,5,6,7,8 k=4)时的结果
(1234 ightarrow 4321)
(12345 ightarrow 45123)
(123456 ightarrow 456321)
(1234567 ightarrow 4567123)
(12345678 ightarrow 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 ight })中第一个不被(p)整除的数为(a_i),(left { b_m ight })中第一个不被(p)整除的数为(b_j),则答案为(i+j)
证明:考虑所有满足(0le 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 ",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 "),0; printf("VALID "); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) printf("%c",ans[i][j]); printf(" "); } }
首先对所有人按(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 ",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^ncdot ans=sum_{i=1}^nsum_{j=i+1}^n p_icdot p_j cdot 2^{n-j+i-1}),稍微化简一下可以得到(2cdot ans=sum_{i=1}^nsum_{j=i+1}^n p_icdot p_j cdot 2^{-j+i}=sum_{i=1}^nsum_{j=i+1}^n p_icdot 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 ",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 ",t[1].ans); } }
upd2:[CodeCraft-20 (Div. 2)][Codeforces 1316F. Battalion Strength]