[20180613]校内模拟赛
T1【Atcoder 2021】
dp。f[i][j]表示前i个小朋友,分j个糖果所计算出来的value。前缀处理1~i的j次幂的和。
f[0][0]=1
f[i][j]=sigma(k=0~j)f[i-1][k]*qz[b[i]][j-k]-qz[a[i]-1][j-k]
然后要注意取模操作。
#include<bits/stdc++.h> using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } #define mod 1000000007 #define MN 405 long long f[MN][MN],pw[MN][MN],qz[MN][MN]; int n,c,a[MN],b[MN]; void add(long long &x,long long y){x+=y;if(x>mod)x-=mod;} long long Abs(long long x){if(x<0) x+=mod;return x;} int main(){ register int i,j,k; for(i=1;i<=400;i++) pw[i][0]=1; for(i=1;i<=400;i++)for(j=1;j<=400;j++) pw[i][j]=(pw[i][j-1]*i)%mod; for(i=1;i<=400;i++)for(j=0;j<=400;j++) qz[i][j]=(qz[i-1][j]+pw[i][j])%mod; n=read(),c=read(); for(i=1;i<=n;i++) a[i]=read(); for(i=1;i<=n;i++) b[i]=read(); f[0][0]=1; for(i=1;i<=n;i++)for(j=0;j<=c;j++)for(k=j;k>=0;k--){ add(f[i][j],f[i-1][k]*Abs(qz[b[i]][j-k]-qz[a[i]-1][j-k])%mod); } printf("%lld\n",f[n][c]); return 0; }
T2:【Atcoder 3911】
首先,并查集各联通块中的最小点权和。
显然,处理联通块的个数为(n-m),每连接一条边就要消耗掉2个点,所以如果(n-m-1)*2>n,就puts("Impossible").
如果n-m=1,答案为0
我们要选出(n-m-1)*2个点,其中,各联通块的最小点必须被取到,剩下取最小的(n-m-2)个点就行了。证明显然。
#include<bits/stdc++.h> using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } #define MN 100005 int n,m,fa[MN],vis[MN],cnt,p[MN],ct; long long a[MN],ans; int findf(int x){return fa[x]==x?fa[x]:fa[x]=findf(fa[x]);} void union_(int x,int y){x=findf(x);y=findf(y);fa[x]=y;return;} vector<int> pic[MN]; int main(){ n=read(),m=read(); register int i,j,x,y; for(i=1;i<=n;i++) a[i]=read(),fa[i]=i; for(i=1;i<=m;i++){ x=read()+1,y=read()+1; union_(x,y); } if(m==n-1) return puts("0"),0; if((n-m-1)*2>n) return puts("Impossible\n"),0; for(i=1;i<=n;i++){ if(vis[findf(i)]) pic[vis[fa[i]]].push_back(a[i]); else pic[vis[findf(i)]=++cnt].push_back(a[i]); } for(i=1;i<=cnt;i++){ sort(pic[i].begin(),pic[i].end()),ans+=pic[i][0]; for(j=1;j<pic[i].size();j++) p[++ct]=pic[i][j]; } sort(p+1,p+ct+1); for(i=1;i<=n-m-2;i++) ans+=p[i]; printf("%lld",ans); return 0;
}
T3:【Atcoder 3967】
我们对4n^2个点进行两次染色处理。
当D为奇数时,显然D只能被分成奇数和偶数的平方和,所以直接将图进行交叉染色,同色的显然距离不会为D。
当D为偶数时,我们通过旋转图,是相邻点的边为原先的对角线,然后D>>=1,通过不断的旋转,直至D是一个奇数。
经过不断的画图和总结,我们发现最终图的染色方法为(用01表示),记D中含有2的指数为k,记squ=2^(k>>1)
如果k为偶数:
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
1 1 1 0 0 0 1 1 1
1 1 1 0 0 0 1 1 1
1 1 1 0 0 0 1 1 1
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
其中,任意一个纯1或纯0的矩阵边长是squ。
如果k为奇数:
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
其中,任意一个纯1或纯0的矩阵行数是squ。
证明的话,多画画图就发现了。
然后对于一个点,它会有4种染色情况(11,10,00,01),根据4种情况将图分成4个集合。
根据平均数的性质,4n^2个点分为4个集合,必定有一个集合它的个数大于等于n^2.
#include<bits/stdc++.h> using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } #define MN 605 pair<int,int> mp[MN<<1]; int N,D1,D2,cc; bool col[MN][MN][2]; struct node{int x,y;}; vector<node> G[5]; void solve(int D,int l){ register int i,j,k,ct=0,squ=1; while(~D&1){ct++;D>>=1;} squ=1<<(ct>>1); if(~ct&1){ for(i=0;i<N*2;i++)for(j=0;j<N*2;j++)col[i][j][l]=((i/squ)+(j/squ))&1; } else{ for(i=0;i<N*2;i++)for(j=0;j<N*2;j++) col[i][j][l]=(i/squ)&1; } } int main(){ register int i,j; N=read(),D1=read(),D2=read(); solve(D1,0),solve(D2,1); for(i=0;i<N<<1;i++)for(j=0;j<N<<1;j++) G[col[i][j][0]+col[i][j][1]<<1].push_back((node){i,j}); for(i=0;i<4;i++)if(G[i].size()>=N*N){ for(j=0;j<N*N;j++) printf("%d %d\n",G[i][j].x,G[i][j].y); return 0; } return 0; }
来自PaperCloud的博客,未经允许,请勿转载,TKS!