8.20T1 星图(拆贡献+dp+暴力卷积)

http://cplusoj.com/d/senior/p/NODSX2301A

给定 n n n 个实数的取值范围,求其笛卡尔树节点深度总和的期望。

场上搞了一大轮区间dp,结果大方向就错了。


拆贡献,考虑 j j j i i i 是否有贡献。若 j j j i i i 有贡献,则 j j j i i i 的祖先,此时贡献为1。所以我们只需要计算 j j j i i i 祖先的概率即可。

考虑 j j j i i i 祖先的充要条件是什么,这点也是我考场没有想到的。如果这点想到了,很容易去往拆贡献那个方向想。

不妨令 j < i j<i j<i,只需满足: ∀ k ∈ [ j + 1 , i − 1 ] , a k < a j \forall k\in[j+1,i-1],a_k<a_j k[j+1,i1],ak<aj

我们对不可拆区间离散化再编号后,设 I ( i , j ) I(i,j) I(i,j) i i i j j j 区间里的概率, U ( i , j ) U(i,j) U(i,j) i i i 在比 j j j 小区间里的概率。不存在就是0。这个是容易易处理的。

我们不妨枚举 j j j 和其所在区间 t t t,然后从 j j j 遍历到 i i i,我们处理一个 d p ( i , x ) dp(i,x) dp(i,x) 表示一直到 i i i,有 x x x 个在 t t t 里,其他都低于 t t t

转移: d p ( i , x ) = d p ( i − 1 , x − 1 ) × I ( i , t ) + d p ( i − 1 , x ) × U ( i , t ) dp(i,x)= dp(i-1,x-1)\times I(i,t)+dp(i-1,x)\times U(i,t) dp(i,x)=dp(i1,x1)×I(i,t)+dp(i1,x)×U(i,t)

此时 j j j i i i 有贡献的概率就是 ∑ x d p ( i , x ) x \sum_x \dfrac{dp(i,x)}{x} xxdp(i,x),因为 j j j 要在和他同层次的数里排第一。

此时复杂度 O ( n 4 ) O(n^4) O(n4)

#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
 #define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
 #define debag(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244353
#define N 410
int pw(int a, int b) {
	int ans = 1; 
	while(b) {
		if(b & 1) ans *= a; 
		a *= a; b >>= 1; 
		ans %= mo; a %= mo; 
	}
	return ans; 
}
int pw(int a) { return pw(a, mo - 2); }
int fac[N], inv[N], ifac[N]; 
void init(int n) {
	int i; 
	for(i = fac[0] = 1; i <= n; ++i) fac[i] = fac[i-1] * i % mo; 
	ifac[n] = pw(fac[n], mo-2); 
	for(i = n - 1; i >= 0; --i) ifac[i] = ifac[i+1] * (i+1) % mo; 
    for(i = 1; i <= n; ++i) inv[i] = ifac[i] * fac[i-1] % mo; 
}
inline void Mod(int &a) { if(a >= mo || a <= -mo) a %= mo; if(a < 0) a += mo; }
inline void Add(int &a, int b) { a += b; Mod(a); }
inline void Mul(int &a, int b) { Mod(b); a *= b; Mod(a); } 
int n, m, i, j, k, T;
int l[N], r[N], R[N << 1], L[N << 1], v[N << 1], ans, a[N], b[N], len[N << 1], Len[N], iLen[N]; 
int Lx[N], Rx[N], In[N][N << 1], Und[N][N << 1]; 
int f[N][N << 1], t; 

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T=read();
//	while(T--) {
//
//	}
	n = read(); init(n); 
	for(i = 1; i <= n; ++i) L[i] = Lx[i] = read(), R[i] = Rx[i] = read(); 
	for(i = 1; i <= n; ++i) Len[i] = R[i] - L[i], iLen[i] = pw(Len[i]); 
	for(i = 1; i <= n; ++i) v[++k] = L[i], v[++k] = R[i]; 
	sort(v + 1, v + 2 * n + 1); reverse(v + 1, v + 2 * n + 1); 
	for(i = 2; i <= 2 * n; ++i){
		if(v[i] != v[i - 1]) l[++m] = v[i], r[m] = v[i - 1], len[m] = r[m] - l[m]; 
	}
	for(i = 1; i <= m; ++i) debug("[%lld %lld] %lld\n", l[i], r[i], len[i]); 
	for(i = 1; i <= n; ++i) {
		int mn = 1e9, mx = 0; 
		for(j = 1; j <= m; ++j) 
			if(L[i] <= l[j] && R[i] >= r[j]) 
				mn = min(mn, j), mx = max(mx, j); 
		L[i] = mn; R[i] = mx; 
		debug("(%lld %lld) %lld %lld\n", L[i], R[i], Len[i], iLen[i]);
	}
	for(i = 1; i <= n; ++i) 
		for(j = 1; j <= m; ++j) {
			if(j < L[i]) In[i][j] = 0, Und[i][j] = 1; 
			else if(j > R[i]) In[i][j] = Und[i][j] = 0; 
			else {
				In[i][j] = len[j] * iLen[i] % mo; 
				Und[i][j] = (l[j] - Lx[i]) * iLen[i] % mo; 
			}
		}
	for(t = 1; t <= m; ++t) 
		for(k = 1; k <= n; ++k) {
			if(!In[k][t]) continue; 
			memset(f, 0, sizeof(f)); 
			f[k][1] = In[k][t] % mo; 
			debug("# %lld : %lld\n", k, f[k][1]); 
			auto work = [&] (int o) -> void {
				for(i = k + o; i <= n && i >= 1; i += o) {
					int P = 0; 
					for(j = 1; j <= abs(k - i) + 1; ++j) {
						Add(f[i][j + 1], f[i - o][j] * In[i][t]); 
						Add(f[i][j], f[i - o][j] * Und[i][t]); 
						Add(P, f[i][j] * inv[j]); 
					}
					debug("## %lld -> %lld %lld(%lld %lld %lld)\n", k, i, P, pw(2), 2 * pw(9), 2 * pw(3) % mo); 
					Add(ans, P); 
				}
			}; 
			work(1); work(-1); 
		}
	printf("%lld", ans); 
	return 0;
}



考虑继续优化,若 t t t 定,我们发现 k k k i i i 的贡献可以表达成下面的形式:

F ( x ) = I ( k ) x ∏ j = k + 1 i ( U ( j ) + I n ( i ) x ) F(x)=I(k)x\prod_{j=k+1}^i (U(j)+In(i)x) F(x)=I(k)xj=k+1i(U(j)+In(i)x)

A n s k → i = ∑ k = 1 ∣ i − j + 1 ∣ [ x k ] F ( x ) k Ans_{k\to i}=\sum_{k=1}^{|i-j+1|} \dfrac{[x^k]F(x)}k Anski=k=1ij+1∣k[xk]F(x)

发现上面就是个卷积形式,我们直接暴力维护即可。

复杂度 O ( n 3 ) O(n^3) O(n3)

#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
 #define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
 #define debag(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244353
#define N 810
int pw(int a, int b) {
	int ans = 1; 
	while(b) {
		if(b & 1) ans *= a; 
		a *= a; b >>= 1; 
		ans %= mo; a %= mo; 
	}
	return ans; 
}
int pw(int a) { return pw(a, mo - 2); }
int fac[N], inv[N], ifac[N]; 
void init(int n) {
	int i; 
	for(i = fac[0] = 1; i <= n; ++i) fac[i] = fac[i-1] * i % mo; 
	ifac[n] = pw(fac[n], mo-2); 
	for(i = n - 1; i >= 0; --i) ifac[i] = ifac[i+1] * (i+1) % mo; 
    for(i = 1; i <= n; ++i) inv[i] = ifac[i] * fac[i-1] % mo; 
}
inline void Mod(int &a) { if(a >= mo || a <= -mo) a %= mo; if(a < 0) a += mo; }
inline void Add(int &a, int b) { a += b; Mod(a); }
inline void Mul(int &a, int b) { Mod(b); a *= b; Mod(a); } 
int n, m, i, j, k, T;
int l[N], r[N], R[N << 1], L[N << 1], v[N << 1], ans, a[N], b[N], len[N << 1], Len[N], iLen[N]; 
int Lx[N], Rx[N], In[N][N << 1], Und[N][N << 1]; 
int f[N][N << 1], t, cnt, F[N << 1]; 

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T=read();
//	while(T--) {
//
//	}
	n = read(); init(n); 
	for(i = 1; i <= n; ++i) L[i] = Lx[i] = read(), R[i] = Rx[i] = read(); 
	for(i = 1; i <= n; ++i) Len[i] = R[i] - L[i], iLen[i] = pw(Len[i]); 
	for(i = 1; i <= n; ++i) v[++k] = L[i], v[++k] = R[i]; 
	sort(v + 1, v + 2 * n + 1); reverse(v + 1, v + 2 * n + 1); 
	for(i = 2; i <= 2 * n; ++i){
		if(v[i] != v[i - 1]) l[++m] = v[i], r[m] = v[i - 1], len[m] = r[m] - l[m]; 
	}
	for(i = 1; i <= m; ++i) debug("[%lld %lld] %lld\n", l[i], r[i], len[i]); 
	for(i = 1; i <= n; ++i) {
		int mn = 1e9, mx = 0; 
		for(j = 1; j <= m; ++j) 
			if(L[i] <= l[j] && R[i] >= r[j]) 
				mn = min(mn, j), mx = max(mx, j); 
		L[i] = mn; R[i] = mx; 
		debug("(%lld %lld) %lld %lld\n", L[i], R[i], Len[i], iLen[i]);
	}
	for(i = 1; i <= n; ++i) 
		for(j = 1; j <= m; ++j) {
			if(j < L[i]) In[i][j] = 0, Und[i][j] = 1; 
			else if(j > R[i]) In[i][j] = Und[i][j] = 0; 
			else {
				In[i][j] = len[j] * iLen[i] % mo; 
				Und[i][j] = (l[j] - Lx[i]) * iLen[i] % mo; 
			}
		}
	for(t = 1; t <= m; ++t) {
		auto work = [&] (int st, int o) -> void {
			for(i = st, cnt = 0; i <= n && i >= 1; i += o, ++cnt) {
				for(j = 0; /*i != st &&*/ j <= cnt; ++j) {
					Add(ans, In[i][t] * F[j] % mo * inv[j + 1] % mo); 
					debug("%lld (%lld %lld)\n", In[i][t] * F[j] % mo * inv[j + 1] % mo, pw(2), 5 * pw(6)); 
				}
				for(j = cnt; j >= 0; --j) 
					Add(F[j + 1], F[j] * In[i][t]), Mul(F[j], Und[i][t]); 
				Add(F[0], Und[i][t]); Add(F[1], In[i][t]); 
//					Add(F[j + 1], (F[j] + 1) * In[i][t]), F[j] = (F[j] + 1) * Und[i][t] % mo; 
				debug("%lld : ", i); for(j = cnt + 1; j >= 0; --j) debug("%lld ", F[j]); debug("\n"); 
			}
		}; 
		memset(F, 0, sizeof(F)); F[0] = 0; work(n, -1); 
		memset(F, 0, sizeof(F)); F[0] = 0; work(1, 1); 
	}
	printf("%lld", ans); 
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值