[THUPC2019]过河卒二

题目

好难啊,不会啊;啊,\(k\leq 20\),那就直接容斥吧;

我们硬点一些控制点,之后使我们必须经过这些被硬点的点;由于我们只能往右上走,所以经过这些被钦定的点的顺序是一定的,我们只需要考虑相邻两点之间的路径数最后乘起来就好;

于是我们需要推一个\(f(n,m)\)表示从\((0,0)\)走到\((n,m)\)的路径数;

如果没有那个斜向上走直接组合数即可,有了这个斜向上走我们就枚举斜向上走了多少次,于是

\[f(n,m)=\sum_{k=0}^m\binom{m+n-2k}{m-k}\binom{m+n-k}{m+n-2k} \]

\(\binom{m+n-2k}{m-k}\)就是走到了\((k,k)\)这个位置之后我们只往右往上走,\(\binom{m+n-k}{m+n-2k}\)就是把\(k\)个斜向上走插到\(n+m-2k\)步里去;

这里的模数特别小,于是我们Lucas一下,就可以\(O(m\log_p n)\)计算\(f(n,m)\),由于\(f\)之后在两两关键点之间用到,于是直接\(O(mk^2\log_p n)\)预处理好即可;

最后我们还需要考虑从最后一个钦定点走出边界的情况,设\(g(n,m)\)表示起点为\((0,0)\),矩形右上角为\((n,m)\)走出边界的方案数;

我们规定从上走出边界的点最后都要走到\((n,m+1)\),从右走出边界的都走到\((n+1,m)\);特殊的,还有在\((n,m)\)位置斜向上走出边界的,于是\(g(n,m)=f(n+1,m)+f(n,m+1)+f(n,m)\)

代码

#include<bits/stdc++.h>
#define re register
const int mod=59393;
struct node{int x,y;}p[25];
inline int dqm(int x){return x<0?x+mod:x;}
inline int qm(int x){return x>=mod?x-mod:x;}
int fac[mod],ifac[mod],inv[mod],ans[25][25],lst[25],fst[25],id[25],n,m,k;
inline int cmp(int a,int b){return p[a].x==p[b].x?p[a].y<p[b].y:p[a].x<p[b].x;}
inline int C(int n,int m) {
	return m>n?0:1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int Lucas(int n,int m) {
	if(m>n)return 0;if(!m)return 1;
	return 1ll*Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
inline int f(int n,int m) {
	if(m<0||n<0)return 0;
	if(m>n)std::swap(n,m);int ans=0;
	for(re int i=0;i<=m;++i)ans=qm(ans+1ll*Lucas(m+n-i,n)*Lucas(n,n-i)%mod);
	return ans; 
}
inline int g(int n,int m) {return qm(qm(f(n+1,m)+f(n,m+1))+f(n,m));}
inline int calc(int state) {
	int cnt=0;
	for(re int i=0;i<k;i++)if(state>>i&1)id[++cnt]=i+1;
	id[++cnt]=0;int nw=(cnt&1?1:mod-1);
	std::sort(id+1,id+cnt+1,cmp);
	for(re int i=1;i<cnt;i++) nw=1ll*nw*ans[id[i]][id[i+1]]%mod;
	return 1ll*nw*lst[id[cnt]]%mod;
}
int main() {
	fac[0]=ifac[0]=inv[1]=1;
	for(re int i=1;i<mod;++i)fac[i]=1ll*fac[i-1]*i%mod;
	for(re int i=2;i<mod;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(re int i=1;i<mod;++i)ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
	scanf("%d%d%d",&n,&m,&k);int tot=0;p[0].x=1,p[0].y=1;
	for(re int i=1;i<=k;i++)scanf("%d%d",&p[i].x,&p[i].y);
	for(re int i=0;i<=k;i++)for(re int j=0;j<=k;j++)
		if(i!=j)ans[i][j]=f(p[j].x-p[i].x,p[j].y-p[i].y);
	for(re int i=0;i<=k;i++) lst[i]=g(n-p[i].x,m-p[i].y);
	for(re int i=0;i<(1<<k);++i) tot=qm(tot+calc(i));
	printf("%d\n",tot);
}
posted @ 2020-01-13 19:52  asuldb  阅读(315)  评论(1编辑  收藏  举报