您的位置:首页 > 其它

【HDOJ 5794】A Simple Chess(大组合数Lucas定理+容斥)

2016-08-05 17:44 190 查看
【HDOJ 5794】A Simple Chess(大组合数Lucas定理+容斥)

A Simple Chess

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1081 Accepted Submission(s): 291

Problem Description

There is a n×mn \times m board, a chess want to go to the position

(n,m)(n,m) from the position (1,1)(1,1).

The chess is able to go to position (x2,y2)(x_2,y_2) from the position (x1,y1)(x_1,y_1), only and if only x1,y1,x2,y2x_1,y_1,x_2,y_2 is satisfied that (x2−x1)2+(y2−y1)2=5, x2>x1, y2>y1(x_2 - x_1)^2+(y_2 - y_1)^2 = 5, ~x_2 > x_1,~ y_2 > y_1.

Unfortunately, there are some obstacles on the board. And the chess never can stay on the grid where has a obstacle.

I want you to tell me, There are how may ways the chess can achieve its goal.

Input

The input consists of multiple test cases.

For each test case:

The first line is three integers, n,m,r,(1≤n,m≤1018,0≤r≤100)n, m, r, (1\leq n, m\leq 10^{18}, 0 \leq r\leq 100), denoting the height of the board, the weight of the board, and the number of the obstacles on the board.

Then follow rr lines, each lines have two integers, x,y(1≤x≤n,1≤y≤m)x, y(1\leq x\leq n, 1\leq y\leq m), denoting the position of the obstacles. please note there aren’t never a obstacles at position (1,1)(1, 1).

Output

For each test case,output a single line “Case #x: y”, where x is the case number, starting from 1. And y is the answer after module 110119110119.

Sample Input

1 1 0

3 3 0

4 4 1

2 1

4 4 1

3 2

7 10 2

1 2

7 1

Sample Output

Case #1: 1

Case #2: 0

Case #3: 2

Case #4: 1

Case #5: 5

Author

UESTC

Source

2016 Multi-University Training Contest 6

根据公式一推,很容易发现是按象棋中马来移动的。

不存在马脚的一说。

然后给出r个陷阱,要求n×mn \times m的图上,不能走到陷阱情况下从点(1,1)(1,1)走到终点(n,m)(n,m)的走法。

已知陷阱不会出现在起点(1,1)(1,1)

首先需要推得对于任意无陷阱的n×mn \times m的图中,走到终点(n,m)(n,m)的走法。

打个表可以看出是杨辉三角,也就是组合数,推到n×mn \times m的图上就是Cnn+m−23−n+m−23−1)C_\frac {n+m-2}{3}^n- \frac {n+m-2}{3}-1)。

但是n和m很大,就要用到Lucas,正经的Lucas我们发现会TLE

因为Lucas里的那个CnmC_m^n也是O(mod)的。因为模数很小,可以用最原始的公式Cnm=m!n!(m−n)!C_m^n = \frac {m!}{n!(m-n)!} 这样可以预处理出来阶乘,因为要取模,逆元也一并用快速幂搞出来即可。

以上是Lucas的部分。

因为要去掉经过陷阱到达(n,m)(n,m)的路径,这里用到一个小容斥。

定义dp[i]dp[i]表示从(1,1)(1,1)不经过陷阱到达第ii个陷阱的路径数目。

预先对陷阱进行先行后列的二级排序。这样保证遍历到第ii个陷阱时,从(1,1)(1,1)出发到ii陷阱过程中可能经过的陷阱之前都已经遍历过了。

dp[i]=从(1,1)到i陷阱的所有路径−不经过其他陷阱到达j陷阱后再到i(中途可以经过其他陷阱)的路径数dp[i] = 从(1,1)到i陷阱的所有路径-不经过其他陷阱到达j陷阱后再到i(中途可以经过其他陷阱)的路径数

这样最终dp[i]dp[i]就表示从(1,1)(1,1)不经过任何陷阱到达ii陷阱的路径数。

可以把(n,m)(n,m)也作为陷阱加进去,这样最后答案就是dp[r]dp[r](dp[0,1,..,r−1]dp[0,1,..,r-1]存陷阱,r为终点)

注意对点(n,m)(n,m)的可达性的判断,因为少了个判断导致对负数进行了组合数运算=。=RE半天

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<LL,LL>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 110119;
const double eps = 1e-8;

LL p[110120];
LL inv[110120];

LL pow_m(LL a,int b)
{
LL ans = 1;
while(b)
{
if(b&1) ans = (ans*a)%mod;
b >>= 1;
a = (a*a)%mod;
}
return ans;
}

void init()
{
p[0] = 1;
for(int i = 1; i < mod; ++i) p[i] = (i*p[i-1])%mod;
for(int i = 0; i < mod; ++i) inv[i] = pow_m(p[i],mod-2);
}

LL C(int m,int n)
{
if(n > m) return (LL)0;
if(m == n) return (LL)1;
return (((p[m]*inv
)%mod)*inv[m-n])%mod;
}

LL Lucas(LL n,LL m)
{
if(m == 0) return(LL)1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}

LL got(LL n,LL m)
{
if(n <= 0 || m <= 0) return 0;
LL mn,mx;
mn = min(m,n);
mx = max(m,n);
LL v = (mn+mx-2)/3;
if((mn+mx-2)%3 || v+1 > mn) return 0;
return Lucas(v,mn-v-1);
}

Pr pr[111];
LL dp[111];

int main()
{
//fread("");
//fwrite("");

LL n,m;
int r,z = 1;
init();

while(~scanf("%lld%lld%d",&n,&m,&r))
{
for(int i = 0; i < r; ++i)
{
scanf("%lld%lld",&pr[i].first,&pr[i].second);
}
sort(pr,pr+r);
pr[r].first = n;
pr[r].second = m;

for(int i = 0; i <= r; ++i)
{
dp[i] = got(pr[i].first,pr[i].second);
for(int j = 0; j < i; ++j)
{
dp[i] = (((dp[i]-(dp[j]*got(pr[i].first-pr[j].first+1,pr[i].second-pr[j].second+1))%mod)%mod)+mod)%mod;
}
}

printf("Case #%d: %lld\n",z++,dp[r]);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: