CODE FESTIVAL 2017 qual A 题解
\(A\)
咕咕
const int N=25;
const char t[]={" YAKI"};
char s[N];int n;
int main(){
scanf("%s",s+1),n=strlen(s+1);
if(n<4)return puts("No"),0;
fp(i,1,4)if(s[i]!=t[i])return puts("No"),0;
puts("Yes");
return 0;
}
\(B\)
如果翻转了\(i\)行\(j\)列,那么变黑的格子个数就是\(i\times m+j\times n-2\times i\times j\),那么枚举行数和列数就行了
int n,m,k;
int main(){
scanf("%d%d%d",&n,&m,&k);
fp(i,0,n)fp(j,0,m)if(k==i*m+j*n-(i*j<<1))return puts("Yes"),0;
puts("No");
return 0;
}
\(C\)
先把行数或列数为奇数时中间多出来的那一行一列去掉,那么一个格子\((i,j)\)必须和其它三个格子相等,记录一下这种四个一相等的次数,以及行列为奇数时中间多出来的那一行中的两个一相等的次数,判断一下是否合法即可
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=105;
char s[N];int c[26],c1,c2,n,m;
inline int min(R int x,R int y){return x<y?x:y;}
int main(){
scanf("%d%d",&n,&m);
fp(i,1,n){
scanf("%s",s+1);
fp(j,1,m)++c[s[j]-'a'];
}
c1=(n>>1)*(m>>1),c2=(n&1)*(m>>1)+(m&1)*(n>>1);
for(R int k,i=0;c1&&i<26;++i){
k=min(c1,c[i]>>2);
c1-=k,c[i]-=(k<<2);
}
if(c1)return puts("No"),0;
for(R int k,i=0;c2&&i<26;++i){
k=min(c2,c[i]>>1);
c2-=k,c[i]-=(k<<1);
}
// printf("%d %d\n",c1,c2);
if(c2)return puts("No"),0;
puts("Yes");
return 0;
}
\(D\)
脑子估计生锈了……
首先把曼哈顿距离转切比雪夫距离,即把\((i,j)\)变成\((i+j,i-j)\),然后我们发现在新图上,一个点到所有与它切比雪夫距离为\(d\)的点必定有横坐标或纵坐标之差为\(d\),那么我们按\(d\times d\)把所有的点分组,每组相同颜色,且与它周围八个相邻的组颜色都不同,这样可以发现必定合法
然后要每组和周围相邻八组颜色都不同,只要奇数行填\(12121212...\),偶数行填\(343434...\)就好了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=505;
const char c[]={"RYGB"};
char mp[N][N];int n,m,d;
int main(){
scanf("%d%d%d",&n,&m,&d);
fp(i,1,n)fp(j,1,m){
R int x=i+j,y=i-j+m-1;
mp[i][j]=c[((y/d)&1)+((x/d)&1)*2];
}
fp(i,1,n){
fp(j,1,m)putchar(mp[i][j]);
putchar('\n');
}
return 0;
}
\(E\)
首先第一次肯定是整行或整列的,那么我们假设第一次是整列,并假设整列涂色的区间为\([l,r]\)(其中\(l,r\)两列必须可以涂色),设这个区间中上下都可以涂色的列的个数为\(k\),那么这一部分的贡献就是\(2^k\)
然后考虑两边,我们强制两边都得先横着涂一次,之后随意。以左边为例,记这一部分朝下的有\(y\)个人,朝上的有\(z\)个人,朝右的有\(x\)个人,如果把横的视为蓝色,纵的视为红色,那么把一个最终的方案的轮廓线给画出来再把后一部分取反,就是从\((0,0)\)走到\(O(x,y+z)\)的方案数\({x+y+z\over x}\),且鉴于横的必须有一个涂满,所以走了\(x-1\)次右之后必须向下,所以最终的方案数为\({x+y+z\over x}-{x+y+z-1\over x}={x+y+z-1\over x-1}\)。然后把前后缀和分别搞出来就行了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
const int N=2e5+5,M=1e6+5;
int fac[M],ifac[M],bin[M],ibin[M];
inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
void init(int n=1e6){
fac[0]=ifac[0]=bin[0]=ibin[0]=1;
fp(i,1,n)fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],P-2);
fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
fp(i,1,n)bin[i]=mul(bin[i-1],2);
fp(i,1,n)ibin[i]=mul(ibin[i-1],499122177);
}
char mp[4][N];int sum[4][N],f[N],b[N],s[N];
int n,m,res;
void calc(){
memset(f,0,sizeof(f));
memset(b,0,sizeof(b));
memset(s,0,sizeof(s));
R int x=sum[0][n],y,z;
fp(i,0,m){
if(mp[2][i+1]=='0'&&mp[3][i+1]=='0')continue;
y=sum[2][i],z=sum[3][i];
f[i]=(x?C(x+y+z-1,x-1):(!y&&!z));
}
x=sum[1][n];
fd(i,m+1,1){
if(mp[2][i-1]=='0'&&mp[3][i-1]=='0')continue;
y=sum[2][m]-sum[2][i-1],z=sum[3][m]-sum[3][i-1];
b[i]=(x?C(x+y+z-1,x-1):(!y&&!z));
}
fp(i,1,m)s[i]=s[i-1]+(mp[2][i]=='1'&&mp[3][i]=='1');
fp(i,0,m)f[i]=mul(f[i],ibin[s[i]]);
fd(i,m+1,1)b[i]=add(b[i+1],mul(b[i],bin[s[i-1]]));
fp(i,1,m)upd(res,mul(f[i-1],b[i+1]));
}
int main(){
// freopen("testdata.in","r",stdin);
init();
scanf("%d%d",&n,&m);
fp(i,0,3)scanf("%s",mp[i]+1);
fp(i,0,3){
R int t=(i<=1?n:m);
fp(j,1,t)sum[i][j]=sum[i][j-1]+(mp[i][j]=='1');
}
if(!sum[0][n]&&!sum[1][n]&&!sum[2][m]&&!sum[3][m])return puts("1"),0;
calc();
swap(mp[0],mp[2]),swap(mp[1],mp[3]);
swap(sum[0],sum[2]),swap(sum[1],sum[3]);
swap(n,m);
calc();
printf("%d\n",res);
return 0;
}
\(F\)
首先如果每个数分别考虑,那么上界肯定是\(\log a_i\)
不过有的操作之间可以合并,一个数是\(2\)的幂次那么所有操作经过它都没关系,否则每一次操作必定只能是往左或者往右
然后贪心,从左往右,记录前面给了后面多少,多余的一次能往前就往前
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5;
int a[N],b[N],c[N],n,res,lst,tmp;
int main(){
scanf("%d",&n);
for(R int i=1,x;i<=n;++i){
scanf("%d",&x),a[i]=x,b[i]=-1;
while(a[i])++b[i],a[i]>>=1;
c[i]=((x&-x)!=x),res+=b[i]+c[i];
}
fp(i,1,n){
tmp=min(lst,b[i]),res-=tmp,lst-=tmp;
if(lst&&c[i])--res,--c[i];
lst=b[i]+c[i];
}
printf("%d\n",res);
return 0;
}