zoukankan      html  css  js  c++  java
  • 【CF559C】Gerald and Giant Chess 题解(动态规划+组合数学)

    题目大意:给定一个H*W的棋盘,棋盘上只有N个格子是黑色的,其他格子都是白色的。在棋盘左上角有一个卒,每一步可以向右或者向下移动一格,并且不能移动到黑色格子中。求这个卒从左上角移动到右下角,一共有多少种可能的路线。$h,wleq 10^5,kleq 2000$。答案对$1e9+7$取模。

    看到数据范围可以看出此题的时间复杂度和$k$有关,自然就想到每个障碍对答案产生了多少贡献。

    考虑路径的组合意义:从左下角走到右下角,先不考虑有障碍的情况,显然是$inom{n+m-2}{m-1}$,表示一共走$n+m-2$步,其中$m-1$步是向右走的。

    现在考虑有障碍的情况:先将所有障碍以$x$为第一关键字,$y$以为第二关键字排序。我们设$f_i$表示从右下角走到第$i$个障碍的方案数。如果不考虑在它右下方的那些障碍,那么答案是$inom{n+m-x_i-y_i}{m-y_i}$。有些路径可能经过在它右下方的障碍,所以要减去它们对答案的影响。所以有:

    $f_i=inom{n+m-x_i-y_i}{y-x_i}-sumlimits_{j=i+1}^k f_j*inom{x_j+y_j-x_i-y_i}{y_j-y_i}$

    最终答案为:

    $ans=inom{n+m-2}{m-1}-sumlimits_{i=1}^k f_i*inom{x_i+y_i-2}{y_i-1}$

    组合数可以$O(n)$预处理逆元,$O(1)$计算。时间复杂度$O(k^2+nlog n)$。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define int long long
    using namespace std;
    const int mod=1e9+7;
    const int maxn=500005;
    int n,m,k,inv[maxn],fac[maxn],ans,f[maxn];
    struct node
    {
        int x,y;
    }a[maxn];
    inline bool cmp(node x,node y){return x.x==y.x?x.y<y.y:x.x<y.x;}
    inline int calc(int x,int y)
    {
        return fac[x]*inv[y]%mod*inv[x-y]%mod;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline int qpow(int x,int y)
    {
        int res=1;
        while(y)
        {
            if (y&1) res=(res*x)%mod;
            x=(x*x)%mod;
            y>>=1;
        }
        return res;
    }
    signed main()
    {
        n=read();m=read();k=read();
        fac[0]=1;
        for (int i=1;i<=n+m;i++) fac[i]=(fac[i-1]*i)%mod;
        inv[n+m]=qpow(fac[n+m],mod-2);
        for (int i=n+m-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; 
        for (int i=1;i<=k;i++) a[i].x=read(),a[i].y=read();
        sort(a+1,a+k+1,cmp);
        ans=calc(n+m-2,m-1);
        for (int i=k;i>=1;i--)
        {
            f[i]=calc(n+m-a[i].x-a[i].y,m-a[i].y);f[i]%=mod;
            for (int j=i+1;j<=k;j++)
                f[i]=(f[i]-f[j]*calc(a[j].x+a[j].y-a[i].x-a[i].y,a[j].y-a[i].y)%mod+mod)%mod;
            ans=(ans-f[i]*calc(a[i].x+a[i].y-2,a[i].y-1)%mod+mod)%mod;
        }
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    高精度除法(到小数点后200位)
    CodeForces-Zuhair and Strings(思维+枚举)
    Codeforces-Salem and Sticks(枚举+思维)
    idata的各个类型
    C51串口的SCON寄存器及工作…
    XCode快捷键
    Objective-C 的 self 和 super 详解 (用简单程序说明问题)
    UITextFieldDelegate委托方法注释
    Objective-C 基础语法log打印那些事儿(一)
    fcntl详细说明
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13661829.html
Copyright © 2011-2022 走看看