这个比赛看起来好像挺重要的,,(结果来了一众大佬谁都打不过qaq)
A,B题不知道是干什么的
C题(其实也是不知道在干什么)就是给一个字母矩阵,重排列后问是否能使它水平轴对称+竖直轴对称。
可以发现一个矩阵最多由三部分组成:1.中心点,需要1个字母;2.中轴线,需要2个相同字母为1对填上去;3.其余部分,需要4个相同字母为1组填上去。显然能填3就能填2,能填2就能填1,因此考虑从3->2->1贪心放即可。
AC代码如下:
#include
using namespace std;
int m,n,num[26]; char s[100009];
int main(){
scanf("%d%d",&m,&n);
int s1=(m>>1)*(n>>1),s2=(m&1)*(n>>1)+(m>>1)*(n&1),s3=m&n&1;
int i,j;
for (i=1; i<=m; i++){
scanf("%s",s+1);
for (j=1; j<=n; j++) num[s[j]-'a']++;
}
for (i=0; i<26; i++){
while (num[i]>=4 && s1){ num[i]-=4; s1--; }
while (num[i]>=2 && s2){ num[i]-=2; s2--; }
while (num[i] && s3){ num[i]--; s3--; }
if (num[i]) break;
}
puts(i<26?"No":"Yes");
return 0;
}
题意就是给你m,n,d,要求构造一个染4色的方案,使得曼哈顿距离恰好为d的任何一对点颜色不同。
首先考虑x+y->x,x-y->y,这样就变成了以(x,y)为中心,边心距为d的正方形的轮廓不能喝(x,y)同色。考虑x所在的行,我们可以采用先d个为一段,换一种颜色再d个为一段,再换回原来的颜色d个一段的方式染。这样可以推广到2维,考虑以一个d*d的正方形为一个单位,然后每4个的颜色两两不同,可以发现满足条件。
AC代码如下:
#include
using namespace std;
char ch[2][2]={{'R','B'},{'G','Y'}}; int n,m,k;
char calc(int x,int y){
int a=x+y,b=x-y;
a+=2000; b+=2000;
a/=k; b/=k; a&=1; b&=1;
return ch[a][b];
}
int main(){
scanf("%d%d%d",&m,&n,&k);
int i,j;
for (i=1; i<=m; i++){
for (j=1; j<=n; j++) printf("%c",calc(i,j)); puts("");
}
return 0;
}
E题题意为给你一个m*n的矩形,在矩形的四周有若干个人,每个人会向对面一直走,知道走到一个被染色的方格。然后在走的过程中他会把脚下的格子染成他自己的颜色。每个人颜色不同。求最后矩形的样子有几种。
首先分上下/左右先走两种情况。考虑上下先走,那么会把矩形分成左右两边。并且可以发现如果最早走的是上下中的第x个和第y个,那么x~y中间的就不能被左右的人走到了。所以可以考虑最早走的是第x~y个,然后左右的人再走。
如果令f[i]表示走了i+1左边的方案数,g[i]表示走了i-1右边的方案数,那么答案就是Σf[i]*g[j]*2^(s[j]-1-s[i])=Σ(f[i]/2^s[i])*(g[j]*2^s[j-1]),其中s[i]表示1~i中有几个是上下都有人的。因此关键是求出f[i],同样考虑左边的先走的是a~b,那么答案就是C(a+c,a)*C(b+d,b),其中c,d表示前i列中上、下个有多少人。考虑C(a+c,a)表示的是用(0,0)走到(a,c)的方案书,那么可以发现答案等价于从(0,0)走到(c+d,t),并且在第c列必须走至少一步向上的方案数,其中t表示左边有几个人。所以答案就是C(c+d+t,t)-C(c+d-1+t,t)。
最后小心特判全0的情况
AC代码如下:
#include
#define N 400009
#define ll long long
#define mod 998244353
#define cbn(x,y) ((ll)fac[x]*inv[y]%mod*inv[(x)-(y)]%mod)
using namespace std;
int m,n,a[N],b[N],fac[N],inv[N]; char A[N],B[N],C[N],D[N];
int solve(int m,int n,char *A,char *B,char *C,char *D){
int i,x,y;
memset(a,0,sizeof(a)); memset(b,0,sizeof(b));
a[0]=b[n+1]=1;
for (i=1,y=0; i<=m; i++) if (A[i]=='1') y++;
for (i=1,x=0; i<=n; i++){
if (C[i]=='1') x++;
if (D[i]=='1') x++;
a[i]=cbn(x+y,x);
if (x) a[i]=(a[i]-cbn(x+y-1,x-1)+mod)%mod;
//cout<0) b[i]=(b[i]-cbn(x+y-1,x-1)+mod)%mod;
//cout<0 && C[i]=='1' && D[i]=='1') tmp=(ll)tmp*(mod+1>>1)%mod;
a[i]=(ll)a[i]*tmp%mod;
if (i<=n && C[i+1]=='0' && D[i+1]=='0') a[i]=0;
}
for (i=1; i<=n; i++) a[i]=(a[i]+a[i-1])%mod;
for (i=tmp=1; i<=n+1; i++){
b[i]=(ll)b[i]*tmp%mod;
if (i<=n && C[i]=='1' && D[i]=='1') tmp=tmp*2%mod;
if (i>1 && C[i-1]=='0' && D[i-1]=='0') b[i]=0;
}
int ans=0;
for (i=1; i<=n; i++) ans=(ans+(ll)b[i+1]*a[i-1])%mod;
//cout<m){
for (i=1; i<=n; i++) if (C[i]=='1' || D[i]=='1') break;
if (i>n){ puts("1"); return 0; }
}
fac[0]=inv[0]=inv[1]=1;
for (i=1; i<=(m+n<<1); i++) fac[i]=(ll)fac[i-1]*i%mod;
for (i=2; i<=(m+n<<1); i++) inv[i]=mod-(ll)inv[mod%i]*(mod/i)%mod;
for (i=2; i<=(m+n<<1); i++) inv[i]=(ll)inv[i-1]*inv[i]%mod;
printf("%d\n",(solve(m,n,A,B,C,D)+solve(n,m,C,D,A,B))%mod);
return 0;
}
F就是已知一个若干个1构成的序列,然后每次可以选择2n个连续的数,按照(1,2)(3,4),...(2n-1,2n)的方式合并,得到最终的序列。现在告诉你最终的序列,求最少几步。
显然应该倒过来做。那么考虑一次操作就是把连续的若干>1的数拆成两半,显然应该贪心地拆成均匀的两份。那么考虑拆x的过程中,每一步之后都是由两部分组成,一部分都是t,另一部分都是t+1。
然后考虑原问题。显然产生的1会将原问题分成两个子问题。显然最小的数最早产生1,那么把原问题分成两个问题之后递归地做。注意到最小的数产生1之后可能还有部分2,需要枚举放在左边还是右边。
AC代码如下:
#include
#define N 100009
using namespace std;
int n,a[N],lg2[N];
struct node{ int x,y; }f[17][N],b[N];
struct matrix{ int p[2][2]; };
bool operator <(node u,node v){ return u.x1; i>>=1){
b[k].x++;
if (i&1) flag=1;
}
b[k].y=flag;
}
matrix solve(int l,int r,int dep){
//cout<r){
matrix x;
x.p[0][0]=0; x.p[0][1]=x.p[1][0]=x.p[1][1]=1;
return x;
}
int mid=getmin(l,r),i,j,k,x=b[mid].x,y=b[mid].y;
matrix u=solve(l,mid-1,x),v=solve(mid+1,r,x),z;
for (i=0; i<2; i++)
for (j=0; j<2; j++){
z.p[i][j]=1000000000;
for (k=0; k<=y; k++) z.p[i][j]=min(z.p[i][j],u.p[i][k]+v.p[y-k][j]);
}
//cout<>1]+1;
for (i=1; i<=n; i++){
scanf("%d",&a[i]); calc(i);
//cout<
by lych
2017.9.25