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 );
}

浙公网安备 33010602011771号