2019 ICPC Asia-East Continent Final 部分题题解

题面

Problem B. Black and White

题目大意:有一个(n) ( imes) (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.( ightarrow ightarrow)
2.(uparrow uparrow)
3.(uparrow ightarrow)
4.( ightarrow uparrow)
前两种情况对(w)无影响,而3,4这两种情况可能会对(w)造成影响。
考虑枚举3,4操作的总次数(p),以及4操作的次数(q)
通过打表,可以发现一个式子:

[lfloor frac{p+1}{2} floor - 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
",solve(n,m,k));
		else printf("%d
",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
*/
原文地址:https://www.cnblogs.com/Purple-wzy/p/13210542.html