题解 [CF708E] Student's Camp

传送门

还是让我先来胡个做法:
考虑区间 DP,令 \(f_{i, j, k}\) 为前 \(i\) 行连通且第 \(i\) 行剩余的块是 \([j, k]\) 中的块的概率
容斥再前缀和再差分一下应该能 \(O(1)\) 转移
那么可以做到 \(O(n^3)\)

然后正解:
上面这个东西其实是可以优化的
但是怎么也看不出来更简略的状态定义了,要怎么优化呢?
考虑上面 DP 的转移式子

\[f(i,l,r) = P(l,r) \left[ \sum_{l' \le r'} f(i-1,l',r') - \sum_{r' \lt l} f(i-1,l',r') - \sum_{l' \gt r} f(i-1,l',r') \right] \]

啊对是从这里抄来的
发现转移只用到上一层的前缀和
而这个前缀和的状态数是 \(O(n^2)\) 级别的
那么直接对前缀和做 DP,推式子部分见原题解
那个 \(S_L(i, r)\) 是怎么想到的呢?
我又没想到我怎么知道
大概是发现直接把 \(L(i, r)\) 带进去会发现左右端点全都不固定好烦啊
然后继续推发现需要先枚举右端点
艹然后继续推发现推出来了不需要 \(S_L(i, r)\)
离谱
复杂度 \(O(nm+k)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, k;
const ll mod=1e9+7;
ll d[N], sum1[N], sum2[N], fac[N], inv[N], f[1510], s[1510][1510], l[1510][1510], a, b, p;
inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

signed main()
{
	n=read(); m=read();
	a=read(); b=read(); p=a*qpow(b, mod-2)%mod;
	k=read();
	fac[0]=fac[1]=1; inv[0]=inv[1]=1;
	for (int i=2; i<=k; ++i) fac[i]=fac[i-1]*i%mod;
	for (int i=2; i<=k; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for (int i=2; i<=k; ++i) inv[i]=inv[i-1]*inv[i]%mod;
	for (int i=0; i<=k; ++i) d[i]=C(k, i)*qpow(p, i)%mod*qpow(1-p, k-i)%mod;
	for (int i=1; i<=m; ++i) sum1[i]=(sum1[i-1]+d[i-1])%mod;
	s[0][m]=1; f[0]=1;
	for (int i=1; i<=n; ++i) {
		for (int j=1; j<=m; ++j) sum2[j]=(sum2[j-1]+d[j-1]*l[i-1][j])%mod;
		for (int j=1; j<=m; ++j) s[i][j]=d[m-j]*((f[i-1]-l[i-1][m-j+1])*sum1[j]%mod-sum2[j])%mod;
		for (int j=1; j<=m; ++j) l[i][j]=(l[i][j-1]+s[i][j-1])%mod;
		for (int j=1; j<=m; ++j) f[i]=(f[i]+s[i][j])%mod;
	}
	printf("%lld\n", (f[n]%mod+mod)%mod);
	
	return 0;
}
posted @ 2022-06-01 15:53  Administrator-09  阅读(2)  评论(0编辑  收藏  举报