您的位置:首页 > 其它

CF #313 (Div. 2) E. Gerald and Giant Chess (dp+Lucas组合计数)

2016-08-06 12:59 363 查看
题目链接

在一个n*m的网格上从(1,1)出发到(n,m)每次只能往右或者往下一步,同时还给出k个坏点,坏点是不可以走到的,问有多少的方式到大(n,m).

经典的题目,记录下来学习,假设目的地是一个特殊的坏点,那么就可以设

dp[i]:表示到达第i个坏点且不经过其他的坏点的方案数。

再看一下这个问题,从(0,0)点到(2,4)点的走法有几个?,答案就是C(2+4,2)或者C(2+4,4)。解释一下,因为每次只能往右一步,或者往下一步,所以,走法就是总的步数中选择往右走的步数次数,或者是往下走的步数的次数。

那么状态转移方程就是dp[i]=C(x[i]+y[i],x[i]) - sum{dp[j]*C(dx+dy,dx) }。其中的dx,dy是点i 到 点j 的 距离差值。

然后再来说一说细节,因为题目的要求也就是只能往右下走,那么我们可以给点排序,那么状态转移方程的j就是在i之前的点了,然后再把终点加入进来,那么答案就是第k个点的方案数,即是dp[k].

最后就是关于组合数的计算了,我们可以预处理出阶乘的逆元,然后就可以计算了,here

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<cstdlib>
#include<vector>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define LL long long
#define pb push_back
#define gcd __gcd

#define For(i,j,k) for(int i=(j);i<k;i++)
#define lowbit(i) (i&(-i))
#define _(x) printf("%d\n",x)

const int maxn = 2e5+200;
const int inf  = 1 << 28;
LL p = 1e9+7;
int n,m,k;

LL fac[maxn],inv[maxn];

LL qsm(LL a, LL b){
LL ans = 1;
while(b){
if(b&1)ans = ans*a%p;
a = a*a%p;
b>>=1;
}
return ans%p;
}
LL C(LL n,LL m){
if(m>n)return 0;
LL ans = fac
;
ans = ans*inv[m]%p * inv[n-m] % p;
return ans;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<maxn;i++){
fac[i] = fac[i-1]*i%p;
inv[i]=qsm(fac[i],p-2)%p;
}
}

LL dp[maxn];
pair<LL,LL> point[maxn];
int main(){
init();
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<k;i++){
scanf("%lld%lld",&point[i].first,&point[i].second);
point[i].second--;point[i].first--;
}
point[k].first=n-1;
point[k++].second=m-1;

sort(point,point+k);

for(int i=0;i<k;i++){
LL sum = 0;
for(int j=0;j<i;j++){
LL dx = point[i].first - point[j].first;
LL dy = point[i].second - point[j].second;
if(dx<0||dy<0)continue;
sum=dp[j]*C(dx+dy,dx)%p+sum%p;
}
dp[i]=C(point[i].first+point[i].second, point[i].first)-sum;
dp[i] = (dp[i] + p) % p;
}
printf("%lld\n",(dp[k-1]+p)%p);

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