2019 ICPC Asia-East Continent Final 部分题题解
Problem B. Black and White
题目大意:有一个\(n\) \(\times\) \(m\)的网格,网格之中的格子有黑白两种颜色。
被(0,0),(0,1),(1,0),(1,1)包围的格子是白色,一个格子周围的四个格子的颜色与其都不相同。
现要从(0,0)走到(n,m),每次只能向上或向右走。
给定\(k\),求满足条件的路径数量,使得这条路径左边的白格子数-黑格子数=\(k\)。
多组数据。
\(n,m,|w| \leq 1e5\),\(T \leq 100\)
题解:
设当前白格子数-黑格子数为\(w\)。
发现每次如果只走一步,我们没法判断当前的\(w\)的变化量。那么考虑走两步。
如果每次都走两步的话,那无非就四种情况:
1.\(\rightarrow \rightarrow\)
2.\(\uparrow \uparrow\)
3.\(\uparrow \rightarrow\)
4.\(\rightarrow \uparrow\)
前两种情况对\(w\)无影响,而3,4这两种情况可能会对\(w\)造成影响。
考虑枚举3,4操作的总次数\(p\),以及4操作的次数\(q\)。
通过打表,可以发现一个式子:
\[\lfloor \frac{p+1}{2} \rfloor - q = k
\]
由此,我们只需要枚举\(p\),就能得到合法的\(q\)了。
现在考虑统计方案数。
设\(x=\frac{n+m}{2}\),那么相当于在\(x\)次操作中任选\(p\)次,又在\(p\)次中任选\(q\)个。
然后情况1,2的组合,就是在\(x-p\)次中选\(\frac{n-p}{2}\)次。也就是三个组合数。
注意所有这些要除以2的地方都要求是偶数才有意义。
如果\(n+m\)是奇数,讨论一下第一步怎么走,然后正常算两次就好了。
时间复杂度:\(O(Tn)\)
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const int Mod=998244353,N=100010;
int T,fac[101000],inv[101000];
I add(int &x,int y){(x+=y)>=Mod?x-=Mod:0;}
IN Plus(int x,int y){(x+=y)>=Mod?x-=Mod:0;return x;}
IN Pow(int x,int y=Mod-2){
re res=1;
while(y){
if(y&1)res=(ll)res*x%Mod;
x=(ll)x*x%Mod;
y>>=1;
}
return res;
}
IN C(int x,int y){if(x<y)return 0;return (ll)fac[x]*inv[y]%Mod*inv[x-y]%Mod;}
I init(){
fac[0]=1;
F(i,1,N)fac[i]=(ll)fac[i-1]*i%Mod;
inv[N]=Pow(fac[N]);
FOR(i,N-1,0)inv[i]=(ll)inv[i+1]*(i+1)%Mod;
}
IN solve(int n,int m,int k){
if(!n||!m)return k==0?1:0;
re x=(n+m)>>1,ans=0;
// cout<<n<<" "<<m<<" "<<k<<" "<<x<<endl;
for(re p=0,q;p<=x;p++){
q=((p+1)>>1)-k;
if(q<0||q>p||((n-p)&1)||p>n)continue;
// cout<<"!"<<p<<" "<<q<<endl;
add(ans,(ll)C(x,p)*C(p,q)%Mod*C(x-p,(n-p)>>1)%Mod);
}
return ans;
}
int n,m,k;
int main(){
read(T);init();
while(T--){
read(n);read(m);read(k);
if(!((n+m)&1))printf("%d\n",solve(n,m,k));
else printf("%d\n",Plus(solve(n-1,m,-k),solve(n,m-1,(n&1)-k)));
}
return 0;
}
/*
5
1 1 0
1 1 -1
2 2 1
2 2 0
4 4 1
*/