• 【CF722E】Research Rover


    题目

    题目链接:https://codeforces.com/contest/722/problem/E
    有一个 (n imes m) 的网格图,图中有 (k) 个特殊点。初始时你有一个权值 (s),并且只能向下或向右走,每经过一个特殊点会使得你的权值除以 (2) (向上取整)。 求从 ((1,1)) 走到 ((n,m)) 时拥有权值的期望。
    (n,mleq 10^5,kleq 2000,sleq 10^6)

    思路

    感觉洛谷题解说的都有问题,其实 (f[i][j]) 就是恰好的方案数,而不是至少的方案数。
    先把特殊点排序。把 ((1,1))((n,m)) 都看作特殊点。
    因为 (n,m) 很大,所以无法直接枚举所有网格。我们发现只有特殊点是会产生贡献的。所以考虑设 (f[i][j]) 表示走到了第 (i) 个特殊点,恰好经过了 (j) 个特殊点的方案数。
    观察到每次经过特殊点是让 (s) 除以 (2),所以经过 (log s) 次后权值就是 (1) 了。所以我们第二维可以只开到 (log s)。这样的话,(f[i][log s]) 则表示至少经过 (log s) 次特殊点的方案数,其他都是恰好。
    考虑转移。对于一个特殊点 (i),假设 (k) 可以转移过来,那么可以插板求出这两个点之间的路径数 (cnt),考虑转移,想直接求出恰好的方案数是很难的,所以可以考虑容斥,记 (g[i][j]) 表示到达第 (i) 个特殊点,经过特殊点数量至少为 (j) 的方案数,那么

    [g[i][j]gets sum_{k} g[k][j-1] imes cnt ]

    因为
    然后就有

    [f[i][j]=g[i][j]-g[i][j+1] (j>1) ]

    其中 (j>1) 的原因是我们已经把 ((1,1)) 看作特殊点,(j=1) 是不可能的,如果让 (j=1) 时有了值,会导致下一个转移容斥出错。
    枚举经过了多少个特殊点再乘上贡献计算答案即可。注意最后答案要乘上 (frac{1}{inom{n+m-1}{n-1}})。因为要求期望。
    时间复杂度 (O(k^2log 10^6))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=2010,M=200010,MOD=1e9+7,LG=22;
    int n,m,t,s,ans,f[N][LG+1];
    ll fac[M],inv[M];
    
    struct node
    {
    	int x,y;
    }a[N];
    
    bool cmp(node x,node y)
    {
    	return (x.x==y.x) ? (x.y<y.y) : (x.x<y.x);
    }
    
    ll fpow(ll x,int k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    ll C(int n,int m)
    {
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    
    void init()
    {
    	fac[0]=inv[0]=1;
    	for (int i=1;i<M;i++) fac[i]=fac[i-1]*i%MOD;
    	inv[M-1]=fpow(fac[M-1],MOD-2);
    	for (int i=M-2;i>=1;i--) inv[i]=inv[i+1]*(i+1)%MOD;
    }
    
    int main()
    {
    	init();
    	scanf("%d%d%d%d",&n,&m,&t,&s);
    	for (int i=1;i<=t;i++)
    		scanf("%d%d",&a[i].x,&a[i].y);
    	a[++t]=(node){1,1}; a[++t]=(node){n,m};
    	sort(a+1,a+1+t,cmp);
    	f[1][1]=1;
    	for (int i=2;i<=t;i++)
    	{
    		for (int j=1;j<i;j++)
    			if (a[j].x<=a[i].x && a[j].y<=a[i].y)
    			{
    				int cnt=C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x);
    				for (int k=1;k<=LG;k++)
    					f[i][k]=(f[i][k]+1LL*f[j][k-1]*cnt)%MOD;
    			}
    		for (int j=2;j<=LG;j++)
    			f[i][j]=(f[i][j]-f[i][j+1])%MOD;
    	}
    	for (int i=2;i<=LG;i++,s=(s+1)/2)
    		ans=(ans+1LL*s*f[t][i])%MOD;
    	ans=1LL*ans*fpow(C(n+m-2,n-1),MOD-2)%MOD;
    	printf("%d",(ans%MOD+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    如何避免自己上传的视频或者音频资源被下载
    定期备份服务器上的项目到本地服务器
    查看项目中的laravel的版本
    PHP高并发和大流量的解决方案
    wordpress的安装及使用
    openstack及组件简要介绍
    Java中 如何把Object类型强转成Map<String, String>类型
    JSch基本使用
    Ganymed SSH-2 for Java
    全面解析NIO
  • 原文地址:https://www.cnblogs.com/stoorz/p/14741867.html
Copyright © 2020-2023  润新知