您的位置:首页 > 其它

Codeforces Gym 100379J Move the (p, q)-knight to the corner! 组合数学DP, Lucas定理

2015-08-14 14:57 225 查看
题目大意:

就是现在一个n*m的棋盘上有一个位于顶点的马(1, 1), 现在要从(1, 1)移动到(n, m), 中间有k个位置是不能走的 k <= 10

并且每次走的时候必须遵循马走的规则: 从(x , y)走到(x + p, y + q)或者 (x + q, y + p)

n, m <= 1e9, k <= 10, p, q <= 20问从起点走到终点必经过坏点的路线有多少种, 结果对d取模 (d <= 1e6)

大致思路:

首先考虑每一个位置(x, y)到达这个位置需要的两种走法的次数分别是t1, t2, 那么 t1*p + t2*q == y, t1*q + t2*p == x

也就是说每一个位置(x, y)都可以转换成坐标(tmpx = 1, tmpy + 1)其中 tmpx = abs(ty*p - tx*q) / abs(p*p - q*q), tmpy = abs(ty*q - p*tx) / abs(q*q - p*p);

也就是将元问题转换成从左上角(1, 1)走到右下角(new_n, new_m)的方案数, 每次向右或者向下走一步(x + 1, y)或(x, y + 1)不经过换点到终点的步数

于是这个DP就和Codeforces 559C一样了, 需要注意的是处理组合数不可能将所有的阶乘以及逆元预处理出来

而组合数C[x][y]中x, y <= 2e9于是需要用到Lucas定理, 因为d <= 1e6于是就处理1e6以内的阶乘就可以了

Lucas定理: Lucas(x, y, mod) = C(x % mod, y % mod)*Lucas(x / mod, y / mod, mod) % mod

代码如下:

Result  :  Accepted     Memory  :  7826 ms     Time  :  62 ms

/*
* Author: Gatevin
* Created Time: 2015/8/14 13:24:30
* File Name: Sakura_Chiyo.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

struct Point
{
lint x, y;
int id;
Point(){}
Point(lint _x, lint _y, int _id)
{
x = _x;
y = _y;
id = _id;
}
};

bool cmp(Point p1, Point p2)
{
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}

Point P[20];

lint p, q, mod;
lint n, m;
int k;
lint dp[100];
lint fac[1000010];

lint quick(lint base, lint pow)
{
lint ret = 1;
while(pow)
{
if(pow & 1) ret = ret*base % mod;
pow >>= 1;
base = base*base % mod;
}
return ret;
}

lint get(lint x, lint y)//C[x][y], x, y <= 1e6
{
return fac[x]*quick(fac[y]*fac[x - y] % mod, mod - 2);
}

//Lucas(x, y, mod) = C(x % mod, y % mod)*Lucas(x/mod, y/mod, mod) % mod
lint C(lint x, lint y)//C[x][y], x, y <= 1e9
{
lint ret = 1;
while(x > 0 && y > 0)
{
if(x % mod < y % mod) return 0;
ret = ret*get(x % mod, y % mod) % mod;
x /= mod, y /= mod;
}
return ret;
}

int main()
{
while(~scanf("%I64d %I64d %I64d", &p, &q, &mod))
{
fac[0] = 1;
for(int i = 1; i <= mod; i++) fac[i] = fac[i - 1]*i % mod;

scanf("%I64d %I64d", &n, &m);
P[0].x = P[0].y = 1;
P[0].id = 0;
int cnt = 0;
scanf("%d", &k);
for(int i = 1; i <= k; i++)
{
lint tx, ty;
scanf("%I64d %I64d", &tx, &ty);
tx--, ty--;
lint tmpx = abs(ty*p - tx*q) / abs(p*p - q*q), tmpy = abs(ty*q - p*tx) / abs(q*q - p*p);
if(tmpx*p + tmpy*q == ty && tmpx*q + tmpy*p == tx)
{
++cnt;
P[cnt] = Point(tmpx + 1, tmpy + 1, cnt);
}
}
n--, m--;
lint tmpx = abs(m*p - n*q) / abs(p*p - q*q), tmpy = abs(m*q - p*n) / abs(q*q - p*p);
if(tmpx*p + tmpy*q != m || tmpx *q + tmpy*p != n)
{
puts("0");
continue;
}
++cnt;
P[cnt] = Point(abs(m*p - n*q) / abs(p*p - q*q) + 1, abs(m*q - p*n) / abs(q*q - p*p) + 1, cnt);
sort(P, P + cnt + 1, cmp);
dp[0] = 1;
for(int i = 1; i <= cnt; i++)
{
dp[i] = C(P[i].x - P[0].x + P[i].y - P[0].y, P[i].x - P[0].x);
for(int j = 1; j < i; j++)
if(P[j].y <= P[i].y)
dp[i] = (dp[i] - dp[j]*C(P[i].x - P[j].x + P[i].y - P[j].y, P[i].x - P[j].x) % mod + mod) % mod;
if(P[i].id == cnt)
{
printf("%I64d\n", dp[i]);
break;
}
}
}
return 0;
}
/*
1 2 997
10 10
3
5 9
6 2
3 2
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息