您的位置:首页 > 其它

HDU-5794-A Simple Chess-容斥加数学推导加大组合数

2016-08-05 15:17 363 查看
题意:要从(1,1)点走到(n,m)点去。只能走马字,而且只能向右下走。地图上还有r个障碍点,不能通过这些点。问走到终点有多少种路径。

思路:首先画图,观察这些点的形状,容易观察,得到马能走的点是一个变形的杨辉三角。我们可以通过坐标转换,将原来坐标的点转换到新的杨辉三角坐标中。

转换公式:

当然要先判断这个点是不是有效点(能不能被马走到)

if((x-1)*2<(y-1)||(y-1)*2<(x-1)||(x+y)%3!=2) 都是无效点

原来的点(x,y)

现在的点(xx,yy)计算方法 (其中我的xx从0开始的yy从1开始的)

t=(x+y)/3;

yy=y-t;

xx=t;

然后就是对一个杨辉三角型进行处理

每个点(包括障碍和终点)从原点(0,1)(转化过了。。)走到的当前点的路径数为C(n,m-1)

先对障碍点进行排序

然后我们记录一个ans数组  ans[i]表示从原点不通过其他障碍到第i个障碍的路径数

容易得到ans[i]=无障碍路径数-ans[j]*(j到i的路径数)  (j<i)

最后, 到达终点的种数为 无障碍路径数-ans[i]*(i到终点的路径)  i<=lr 

组合数比较大要用一个模版

注意如果有障碍在终点上,答案是0

#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct NODE {
long long x,y;
}c[111];
long long ans[111];

/**大组合数模版**/
const long long p = 110119;
long long PowMod (LL a,LL b,LL MOD){
LL ret=1;
while(b){
if(b&1) ret=(ret*a)%MOD;
a=(a*a)%MOD;
b>>=1;
}
return ret;
}
long long fac[p+10];
void init () {
fac[0]=1;
for(int i=1;i<=p;i++)
fac[i]=(fac[i-1]*i)%p;
}
long long C (LL n,LL m){
if (m > n)
return 0;
LL ret=1;
while(n&&m){
LL a=n%p,b=m%p;
if(a<b) return 0;
ret=(ret*fac[a]*PowMod(fac[b]*fac[a-b]%p,p-2,p))%p;
n/=p;
m/=p;
}
return ret;
}
/**大组合数模版**/

bool cmp(const NODE A,const NODE B) {
if(A.x==B.x) return A.y<B.y;
return A.x<B.x;
}
bool pan(long long x,long long y) { ///判断这个点是不是有效点
if((x-1)*2<(y-1)) return 0;
if((y-1)*2<(x-1)) return 0;
if((x+y)%3!=2) return 0;
return 1;
}
int main()
{
init();
int kase=0;
long long n,m;
int r;
while(scanf("%lld%lld%d",&n,&m,&r)!=EOF) {
int lr=0;
int fa=1;
for(int i=1;i<=r;i++) {
long long txx,tyy;
scanf("%lld%lld",&txx,&tyy);
if(txx==n&&tyy==m) { ///如果有障碍在终点上
fa=0;
}
if(pan(txx,tyy)&&(txx+tyy<n+m)) { ///排除那些在终点后面的障碍
lr++;
c[lr].x=txx;
c[lr].y=tyy;

}
}
if(fa==0||!pan(n,m)) {
printf("Case #%d: 0\n",++kase);
continue;
}
/**点转化**/
long long tt;
for(int i=1;i<=lr;i++) {
tt=(c[i].x+c[i].y)/3;
c[i].y=c[i].y-tt;
c[i].x=tt;
}
tt=(n+m)/3;
m=m-tt;
n=tt;

sort(c+1,c+lr+1,cmp);///障碍点排序
for(int i=1;i<=lr;i++) {
ans[i]=C(c[i].x,c[i].y-1);
for(int j=1;j<i;j++) {
if(c[i].y<c[j].y) continue;
if(c[i].x==c[j].x) continue;
ans[i]=(ans[i]-ans[j]*C(c[i].x-c[j].x,c[i].y-c[j].y)%p+p)%p;///计算路径数
}
}
long long tot=0;
tot=C(n,m-1);
for(int i=1;i<=lr;i++) {
if(m<c[i].y) continue;
tot=(tot-ans[i]*C(n-c[i].x,m-c[i].y)%p+p)%p;
}
printf("Case #%d: %lld\n",++kase,tot);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息