1 条题解

  • 0
    @ 2025-8-24 22:10:08

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar huayucaiji
    I was once great.The glory is bound to be back one day!

    搬运于2025-08-24 22:10:08,当前版本为作者最后更新于2021-03-21 08:51:23,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    其实这个题蛮简单的。

    作为一个国家抬杠二级运动员,我们要杠一下这句话:

    请注意,上述背景内容与本题无关!

    我非要让你有关

    考虑普通的过河卒,若没有马(不是骂人),那么从 (0,0)>(n,m)(0,0)->(n,m),的方案数是 Cn+mnC_{n+m}^n。其意义是,我总共走 n+mn+m 步,其中 nn 步向上,选出这 nn 步的位置就是方案数。

    现在我们还可以沿着斜对角走,我们枚举斜着走的方案数:

    $$ans=\sum\limits_{i=0}^{\min(n,m)} C_{n+m-i}^i\cdot C_{n+m-2*i}^{n-i} $$

    我们回归题目,题目现在是 (1,1)>(n,m)(1,1)->(n,m)。题目里说:

    其次,过河卒二的终点不再是一个特定的位置,Kiana 规定卒可以从棋盘的上方或右方走出棋盘,此时就视为游戏成功。注意在走出棋盘时仍然有方向选择的不同。

    我们可以认为是从 (1,1)>(n+1,m+1)(1,1)->(n+1,m+1) 呀,理解为加上一行一列。

    但是现在还有一个疑难没解决,就是障碍的问题。可以用容斥原理来计算。但是时间会炸。可以先预处理。我们对每个障碍按横坐标排序,排序后计算出任意两个之间方案数,带入公式:

    ans=Scalc(S)×(1S)ans=\sum\limits_{S} calc(S)\times (-1^{|S|})

    由于模数是质数,可以用 Lucas 定理解决。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=1e5+10,MOD=59393; 
    
    int n,m,k;
    int jc[MAXN],f[21][21],invjc[MAXN];
    struct point {
    	int x,y;
    }p[21];
    
    bool cmp(point a,point b) {
    	if(a.x!=b.x) {
    		return a.x<b.x;
    	}
    	return a.y<b.y;
    }
    
    int qpow(int x,int y) {
    	int ret=1;
    	while(y) {
    		if(y&1) {
    			ret=ret*x%MOD;
    		}
    		x=x*x%MOD;
    		y>>=1;
    	}
    	return ret;
    }
    int inv(int x) {
    	return qpow(x,MOD-2);
    }
    
    int C(int x,int y) {
    	if(y>x) {
    		return 0;
    	}
    	return jc[x]*invjc[y]%MOD*invjc[x-y]%MOD;
    }
    
    int lucas(int x,int y) {
    	if(y==0) {
    		return 1;
    	}
    	return lucas(x/MOD,y/MOD)*C(x%MOD,y%MOD)%MOD;
    }
    
    int fff(int n,int m) {
    	if(min(n,m)<0) {
    		return 0;
    	}
    	int ret=0,ub=min(n,m);
    	for(int i=0;i<=ub;i++) {
    		ret=(ret+lucas(n+m-i,i)*lucas(n+m-i*2,n-i)%MOD)%MOD;
    	}
    	return ret;
    	
    }
    
    int calc(int s) {
    	if(!(s&(1<<0))||!(s&(1<<k))) {
    		return 0;
    	}
    	int lst[22]={},cnt=0,times=1;
    	for(int i=0;i<=k;i++) {
    		if(s&(1<<i)) {
    			lst[++cnt]=i;
    			times=times*-1;
    		}
    	}
    	times=(times+MOD)%MOD;
    	for(int i=2;i<=cnt;i++) {
    		times=times*f[lst[i-1]][lst[i]]%MOD;
    	}
    	return times;
    }
    
    signed main() {
    	jc[0]=1;
    	invjc[0]=1;
    	for(int i=1;i<=MOD;i++) {
    		jc[i]=jc[i-1]*i%MOD;
    		invjc[i]=inv(jc[i]);
    	}
    	cin>>n>>m>>k;
    	n++;
    	m++;
    	for(int i=1;i<=k;i++) {
    		p[i].x=read();
    		p[i].y=read();
    	}
    	p[0].x=1;p[0].y=1;
    	k++;
    	p[k].x=n;
    	p[k].y=m;
    	sort(p+1,p+k+1,cmp);
    	
    	for(int i=0;i<=k;i++) {
    		for(int j=i+1;j<=k;j++) {
    			f[i][j]=fff(p[j].x-p[i].x,p[j].y-p[i].y);
    		}
    	}
    	
    	int ans=0;
    	for(int i=0;i<(1<<k+1);i++) {
    		ans=(ans+calc(i))%MOD;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    
    • 1

    信息

    ID
    4362
    时间
    1000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者