【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;
}
原文地址:https://www.cnblogs.com/stoorz/p/14741867.html