codeforces 722E. Research Rover

题目链接:http://codeforces.com/problemset/problem/722/E
题目大意:有一个n*m的网格,起点(1,1),终点(n,m),每次随机向右或向下走。一开始的能量为s,其中有k个网格异常,经过这样的网格会使能量从原来的x变为x/2的上取整(即(x+1)/2)。求最后到达(n,m)时的期望能量值。
数据范围:1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 2000, 1 ≤ s ≤ 1 000 000

题解:由于s每次遇到异常的网格都减半,最多减logs次就变成1,这样能量值最多有20种,所以我们只需要统计走到终点时每种能量值的方案数即可。
首先将点的坐标以x为第一关键字,y为第二关键字排序,然后在最前面添一个点(1,1)(即第0个点),从后往前进行dp(由于一开始想叉了写了从后往前的dp,这里也就依照代码进行分析,事实上从前往后也一样。)
为了方便,令d[i][j]表示从点 i 走到点 j 的方案数,即d[i][j]=C(p[j].x-p[i].x+p[j].y-p[i].y,p[j].x-p[i].x)。
设g[i][j]表示从点(n,m)走到第 i 个点,途中恰好经过了 j 个异常点(不包括点 i )的方案数。直接求g不太好求,那么我们再设f[i][j]表示经过了不超过j个点的方案数,这样只要在总方案数中减去超过了 j 个点的方案数即可。所以我们枚举经过的第 j 个异常点,则f[i][j]=d[i][(n,m)]-∑(g[l][j]*d[i][l]),g[i][j]=f[i][j]-f[i][j-1],其中p[i].x<=p[l].x且p[i].y<=p[l].y。
计算出所有的方案数之后,最后答案为(∑g[0][i]*a[i])/d[(1,1)][(n,m)],a[i]表示经过 i 个异常点后s的值。
时间复杂度O(n^2logs)

代码如下:

#include 
#include 
using namespace std;
const int mo=1000000007;
int a[30],fac[200005],ine[200005],g[2005][30];
struct P{
    int x,y;
}b[2005];
bool operator <(P a,P b){
    if (a.x!=b.x) return a.xx;
    return a.yy;
}
int C(int x,int y){
    return 1ll*fac[x]*ine[y]%mo*ine[x-y]%mo;
}
int qsm(int x,int y){
    int i=1;
    for (;y;y>>=1,x=1ll*x*x%mo)
        if (y&1) i=1ll*i*x%mo;
    return i;
}
int main(){
    int n,m,k,s;
    scanf("%d%d%d%d\n",&n,&m,&k,&s);
    fac[0]=1;
    for (int i=1;i<=n+m;i++) fac[i]=1ll*fac[i-1]*i%mo;
    ine[n+m]=qsm(fac[n+m],mo-2);
    for (int i=n+m;i>=1;i--) ine[i-1]=1ll*ine[i]*i%mo;
    int len=0;a[0]=s;
    for (len=1;s!=1;len++) a[len]=s=(s+1)/2;
    len--;
    for (int i=1;i<=k;i++) scanf("%d%d\n",&b[i].x,&b[i].y);
    sort(b+1,b+k+1);b[0].x=b[0].y=1;
    for (int i=k;i>=0;i--){
        for (int j=0;jx+m-b[i].y,n-b[i].x);
            for (int l=k;l>i;l--)
                if (b[i].y<=b[l].y)
                    g[i][j]=(g[i][j]-1ll*g[l][j]*C(b[l].x-b[i].x+b[l].y-b[i].y,b[l].x-b[i].x))%mo;
        }
        g[i][len]=C(n-b[i].x+m-b[i].y,n-b[i].x);
        for (int j=len;j>=1;j--) g[i][j]=(g[i][j]-g[i][j-1])%mo;
    }
    int ans=0;
    for (int i=0;i<=len;i++)
        ans=(ans+1ll*(g[0][i]+mo)*a[i])%mo;
    ans=1ll*ans*qsm(C(n+m-2,n-1),mo-2)%mo;
    printf("%d\n",ans);
}

你可能感兴趣的:(codeforces,dp,组合数学)