专栏文章

题解:P5376 [THUPC 2019] 过河卒二

P5376题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mipntp8n
此快照首次捕获于
2025/12/03 15:02
3 个月前
此快照最后确认于
2025/12/03 15:02
3 个月前
查看原文

题意

卒从 (1,1)(1,1) 出发,可以向令 x+1x+1,或 y+1y+1,或 x+1x+1 并且 y+1y+1。棋盘上有一些地方不可到达,求到达 (n+1,m+1)(n+1,m+1) 的方案数(原文不是这样的,但是转化之后是的)。

思路

首先考虑没有障碍时怎么做。
dpi,jdp_{i,j} 表示在没有障碍的情况下,到达 (i,j)(i,j) 的方案数。
如果只有 x+1x+1y+1y+1 两个操作是好做的,走斜线就比较烦人。
所以考虑枚举走的斜线数量。
直接给出 dpi,jdp_{i,j} 的计算方法。
dpi+1,j+1=k=0min(i,j)Ci+jk×2ik×Ci+jkkdp_{i+1,j+1}=\sum_{k=0}^{\min(i,j)} C_{i+j-k \times 2}^{i-k} \times C_{i+j-k}^{k}
如果有点想不明白,可以先假设斜线是在一起的,再将其与直线混在一起,求出组合数就行了。
接下来加上障碍。
考虑容斥原理(其实挺显然的吧)。
注意到 kk 只有 2020,考虑暴力枚举经过了哪几个障碍即可。
具体的,先将障碍物按照 xx 为第一关键字,yy 为第二关键字从小到达排序,预处理出两两之间的方案数(与上文的类似)。
然后状态压缩一下就行了。
CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
bool Beginning;

#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define se second
#define fi first
using PII=pair<int,int>;
using PIB=pair<int,bool>;
using PBI=pair<bool,int>;
using PBB=pair<bool,bool>;
using PDI=pair<double,int>;
using PID=pair<int,double>;

const int mod=59393;
namespace Memory_Love{
	int read(){ int x=0; bool flag=1; char ch=getchar();
		while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
		while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return flag? x:-x;}
	template<typename T> void read(T &x){ bool flag=1; x=0; char ch=getchar();
		while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
		while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} x=(flag? x:-x);}
	template<typename T,typename ...Args> void read(T &Re,Args &...Res){read(Re),read(Res...);}
	template<typename T> void write(T x,char ch=0){if(x<0) x=-x,putchar('-');
		static short s[35],top=-1; do{s[++top]=x%10;x/=10;}while(x);
		while(~top) putchar(s[top--]+48); if(ch) putchar(ch);}
	int gcd(int a,int b){return b==0? a:gcd(b,a%b);}
	int lcm(int a,int b){return a/gcd(a,b)*b;}
	void MADD(int &x,int y,int mod){x=(x+y>=mod? x+y-mod:x+y);}
	int ksc(int a,int b,int p){int ans=0;while(b){if(b&1)MADD(ans,a,p);MADD(a,a,p);b>>=1;}return ans;}
	int ksm(int a,int b,int p){int ans=1%p;while(b){if(b&1)ans=ans*a%p;a=a*a%p;b>>=1;}return ans;}
	int exgcd(int a,int b,int &x,int &y){if(b==0){x=1,y=0;return a;}int d=exgcd(b,a%b,y,x);y-=a/b*x;return d;}
	int inv(int t){int x,y;exgcd(t,mod,x,y);return (x%mod+mod)%mod;}
} using namespace Memory_Love;
const int N=1e5+5;
const int MK=22;
int n,m,k;
int B[MK][MK];

namespace FACT
{
	int fact[mod+5],infact[mod+5];
	void init(int n)
	{
		int i;
		for(i=fact[0]=infact[0]=1;i<=n;i++)
		{
			fact[i]=fact[i-1]*i%mod;
			infact[i]=infact[i-1]*ksm(i,mod-2,mod)%mod;
		}
	}
	int C(int n,int m)
	{
		if(n<m) return 0;
		return fact[n]*infact[m]%mod*infact[n-m]%mod;
	}
	int Lucas(int n,int m)
	{
		if(m==0) return 1;
		return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
	}
} using FACT::Lucas;

struct Barrier
{
	int x,y;
}a[MK];
bool comp(const Barrier &a,const Barrier &b)
{
	if(a.x!=b.x)
	return a.x<b.x;
	else 
	return a.y<b.y;
}

int DP(int n,int m)
{
	int i,ans=0; n--,m--;
	for(i=0;i<=min(n,m);i++)
	(ans+=Lucas(n+m-i*2,n-i)*Lucas(n+m-i,i))%=mod;
	return ans;
}

bool Ending;
signed main()
{
	int i,j;
	read(n,m,k);
	FACT::init(mod-1);
	for(i=1;i<=k;i++)
	read(a[i].x,a[i].y);
	sort(a+1,a+1+k,comp);
	a[0]=(Barrier){1,1};
	a[k+1]=(Barrier){n+1,m+1};
	
	for(i=0;i<=k+1;i++)
	{
		for(j=i+1;j<=k+1;j++)
		{
			if(a[i].y>a[j].y)
			B[i][j]=0;
			else
			B[i][j]=DP(a[j].x-a[i].x+1,a[j].y-a[i].y+1);
		}
	}
	
	int ans=DP(n+1,m+1);
	for(i=0;i<(1<<k);i++)
	{
		if(i==0) continue;
		int now=1,last=0,t=-1;
		for(j=0;j<k;j++)
		{
			if(i>>j&1)
			{
				(now*=B[last][j+1])%=mod;
				last=j+1;
				t=-t;
			}
		}
		(ans-=now*B[last][k+1]*t)%=mod;
	}
	write((ans%mod+mod)%mod,'\n');
	cerr<<"\nused:"<<(abs(&Ending-&Beginning)/1048576)<<"MB\n";
	return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...