[Codeforces 722E]Research Rover

题目大意:

给出一个n*m的方格阵,从(1,1)走到(n,m),只能向下或向右走,(1,1)会有一个初始分数s。在整个图中有k个特殊点,每经过一个点,都会将目前剩余的分数除以2且向上取整。求(1,1)到(n,m)的期望得分。

1<=n,m<=1e5;0<=k<=2000;1<=s<=1e6;


 

 

DP+容斥。

 

  • 要求期望就好好求嘛,反正我看不懂某篇题解为什么答案只算经过k个特殊点的方案数
  • 由于n,m<=1e5,显然不能暴力枚举点,yy出复杂度不含n、m. ,所以我们可以枚举特殊点。
  • 仔细分析,发现当k>log2s的时候s到(n,m)的值就为1了。所以最多枚举20个特殊点左右。
  • 枚举特殊点就要将特殊点排序,那么就以横坐标为第一关键字,以纵坐标为第二关键字。
  • 然后就容斥搞啊
  • 倒着来。
  • 噢还有,这道题要卢卡斯定理、逆元。

 

  令d[i][j]表示从点i走到点j的方案总数,根据小学组合数我们可以知道d[i][j] = C(a[j].x-a[i].x+a[j].y-a[j].x , a[j].x - a[i].x)

设g[i][j]表示从点i到(n,m)恰好经过j个障碍点(不包括点i)的方案数。

显然很难求。那么我们添加一个辅助变量,f[i][j]表示从点i到点(n,m)至少经过j个点的方案数。

所以f数组的求法就是将总数减去超过j个点的方案数。

f[i][j] = d[i][(n,m) - ∑(g[k][j]*d[i][k]),满足a[k].x >= a[i].x,a[k].y>=a[i].y;

所以,g[i][j] =f[i][j] - f[i][j-1]。

有点类似与前缀和。

 

代码里合并在一起了。

 

Code:

#include<bits/stdc++.h>
using namespace std;

const int mod = 1000000007;
const int maxn = 2e5+5;

int a[30],fac[maxn],ine[maxn],g[maxn][30];

struct node{
    int x,y;
}e[3000];

bool cmp(node a,node b){
    return a.x == b.x?a.y < b.y:a.x < b.x;
}

int C(int x,int y){
    return 1ll * fac[x]*ine[y]%mod*ine[x-y]%mod;
}

int qpow(int x,int y){
    int cnt = 1;
    while(y){
        if(y&1)cnt = 1ll*cnt*x%mod;
        y>>=1;x = 1ll*x*x%mod;
    }
    return cnt;
}

int main(){
    int n,m,k,s;
    cin>>n>>m>>k>>s;
    fac[0] = 1;
    for(int i = 1;i<=n+m;++i)fac[i] = 1ll*fac[i-1]*i%mod;
    ine[n+m] = qpow(fac[n+m],mod-2);
    for(int i = n+m;i>=1;--i)ine[i-1] = 1ll*ine[i]*(i)%mod;
    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)cin>>e[i].x>>e[i].y;
    sort(e+1,e+k+1,cmp);e[0].x = e[0].y = 1;
    for(int i = k;i>=0;--i){
        for(int j = 0;j<len;++j){
            g[i][j] = C(n-e[i].x+m-e[i].y,n-e[i].x);
            for(int l = k;l>i;--l){
                if(e[i].y <= e[l].y){
                    g[i][j] = (g[i][j]-1ll*g[l][j]*C(e[l].x-e[i].x+e[l].y-e[i].y,e[l].x-e[i].x))%mod;
                }
            }
        }//上面的g数组相当于f数组。
        g[i][len] = C(n-e[i].x+m-e[i].y,n-e[i].x);
        for(int j = len;j>=1;--j)g[i][j] = (g[i][j]-g[i][j-1])%mod;
    }
    int ans = 0;
    for(int i = 0;i<=len;++i)ans = (ans + 1ll*(g[0][i]+mod)*a[i])%mod;
    ans = 1ll*ans*qpow(C(n+m-2,n-1),mod-2)%mod;
    cout<<ans<<endl;
}

 

 

 

上一篇:洛谷P5437 约定(概率期望,拉格朗日插值,自然数幂)


下一篇:[HAOI 2012] 容易题