【CF722E】Research Rover

题目

题目链接:https://codeforces.com/contest/722/problem/E
有一个 \(n\times m\) 的网格图,图中有 \(k\) 个特殊点。初始时你有一个权值 \(s\),并且只能向下或向右走,每经过一个特殊点会使得你的权值除以 \(2\) (向上取整)。 求从 \((1,1)\) 走到 \((n,m)\) 时拥有权值的期望。
\(n,m\leq 10^5,k\leq 2000,s\leq 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]\times cnt \]

因为
然后就有

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

其中 \(j>1\) 的原因是我们已经把 \((1,1)\) 看作特殊点,\(j=1\) 是不可能的,如果让 \(j=1\) 时有了值,会导致下一个转移容斥出错。
枚举经过了多少个特殊点再乘上贡献计算答案即可。注意最后答案要乘上 \(\frac{1}{\binom{n+m-1}{n-1}}\)。因为要求期望。
时间复杂度 \(O(k^2\log 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;
}
posted @ 2021-05-07 17:19  stoorz  阅读(252)  评论(0编辑  收藏  举报