SP16639 IE4 - Endless Knight 题解

SP16639 IE4 - Endless Knight 题解

题目分析

本题要求计算骑士从 \((1, 1)\) 跳到 \((h, w)\) 的方案数,骑士只能向右下跳,每次跳跃有两种选择:\((+1, +2)\)\((+2, +1)\)。同时存在若干禁止点,路径不能经过这些点。结果对 \(10007\) 取模。

核心思路

1. 路径计数问题

设骑士进行了 \(a\)\((+1, +2)\) 跳跃和 \(b\)\((+2, +1)\) 跳跃,则:

\[\begin{cases} a + 2b = x \\ 2a + b = y \end{cases} \]

其中 \((x, y)\) 是目标点相对于起点的坐标偏移(即 \(x = h-1, y = w-1\))。

解得:

\[a = \frac{2x - y}{3}, \quad b = \frac{2y - x}{3} \]

变形可得:

\[a = x - \frac{x+y}{3}, \quad b = y - \frac{x+y}{3} \]

有效路径的条件:

  • \((x + y)\) 必须能被 \(3\) 整除
  • \(a \geq 0\)\(b \geq 0\)

方案数为组合数 \(C(a+b, a)\),即从 \(a+b\) 步中选择 \(a\) 步走 \((+1, +2)\)

2. Lucas 定理

由于 \(h, w\) 可达 \(10^8\),直接计算组合数不可行,需使用 Lucas 定理:

\[C(n, m) \equiv \prod_{i=0}^{k} C(n_i, m_i) \pmod{p} \]

其中 \(n_i, m_i\)\(n, m\)\(p\) 进制下的各位数字。

3. 容斥原理处理禁止点

设禁止点为 \(z_1, z_2, \dots, z_r\),按坐标排序后使用动态规划:

  • \(dp[i]\) 表示从起点到 \(z_i\) 且不经过其他禁止点的方案数
  • 最终答案 = 总方案数 \(-\) 经过至少一个禁止点的方案数

\[dp[i] = f(z_i) - \sum_{j < i} dp[j] \cdot f(z_i - z_j) \]

\[ans = f(h-1, w-1) - \sum_{i=1}^{r} dp[i] \cdot f((h,z_i.x), (w,z_i.y)) \]

复杂度分析

总体复杂度可以近似看成 \(O(nr^2)\),在 vjudge 上用时 \(10ms\)

代码

我的码风真差

#include<bits/stdc++.h>
using namespace std;
int a,b,w,h,num,fac[10010],inv[10010],t;
long long dp[15],ans;
struct op{
    int x,y;
}rok[12];
int pow(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=res*a%10007;
        a=a*a%10007;
        b>>=1;
    }
    return res;
}
bool cmp(op u,op v){
	if(u.x==v.x)return u.y<v.y;
	return u.x<v.x;
}
long long C(int a,int b){
	if(a<b) return 0;
	return fac[a]*inv[b]%10007*inv[a-b]%10007;
}
long long lucas(int a,int b){
	if(a<b)return 0;
	if(b==0)return 1;
	return C(a%10007,b%10007)*lucas(a/10007,b/10007)%10007;
}
int f(int x, int y) {
	if(x==0&&y==0)return 1;
	int a=x-(x+y)/3,b=y-(x+y)/3;
	if(a<0||b<0||(x+y)%3!=0)return 0;
	return lucas(a+b,a);
}
int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    fac[0]=1,inv[0]=1;
    for(int i=1;i<=10007;i++)fac[i]=fac[i-1]*i%10007,inv[i]=pow(fac[i],10005);
    cin>>t;
    for(int o_O=1;o_O<=t;o_O++){
        cin>>h>>w>>num,cout<<"Case #"<<o_O<<": ",memset(rok,0x3f3f3f3f,sizeof(rok)),memset(dp,0,sizeof(dp)),ans=0;
        for(int i=1;i<=num;i++)cin>>rok[i].x>>rok[i].y;
        sort(rok+1,rok+num+1,cmp),ans=f(h-1,w-1);
        for(int i=1;i<=num;i++){
            dp[i]=f(rok[i].x-1,rok[i].y-1);
			for(int j=1;j<i;++j){
				if(rok[j].x<rok[i].x&&rok[j].y<rok[i].y){
					dp[i]=(dp[i]-dp[j]*f(rok[i].x-rok[j].x,rok[i].y-rok[j].y)%10007+10007)%10007;
				}
			}
			ans=(ans-dp[i]*f(h-rok[i].x,w-rok[i].y)%10007+10007)%10007;
        }
        cout<<ans<<"\n";
    }
    return ( 0 - 0 );
}
posted @ 2026-07-16 16:02  ブルーヘイロー  阅读(5)  评论(0)    收藏  举报