首页 > 其他分享 >P10141

P10141

时间:2024-02-22 22:24:59浏览次数:26  
标签:int ll P10141 ans inv dp MOD

P10141

题解

  • 正难则反
  • 正着,设 \(dp(l, r)\) 为合并出 \([l, r]\)的概率,枚举大区间两端点合并(区间DP)
    • 复杂度 \(O(n^4)\)
  • 反着,设 \(dp(l, r)\) 表示由 \([1, n]\) 分裂出 \([l, r]\) 的概率
    • 设大区间为 \([i, j]\),中间点为 \(k\)
    • 若 \([i, k] > [k + 1, j]\),\(dp(i, k) = \frac{dp(i, j)}{i - j}\) (一共有 \(i - j\) 个空隙,选择其中一个的概率是 \(\frac{1}{i - j}\))
    • 反之,\(dp(k + 1, j) = \frac{dp(i, j)}{i - j}\)
    • 这里 \([i, k] > [k + 1, j]\)​ 的比较规则见题意
  • 如果 \(i\) 固定,使得 \([k + 1, j] < [i, k]\) 的 \(j\) 单点递减
  • 同样,如果 \(j\) 固定,使得 \([i, k] < [k + 1, j]\) 的 \(i\)​ 单调递增
  • 所以可以采用双指针的方法,维护前/后缀和,把复杂度降到 \(O(n^2)\)
  • 此时 \(dp(i, k) = \sum_{p = k + 1}^{j} \frac{dp(i, p)}{p - i}\)

代码

  • 小细节

    • 为什么不是 \(\le\)

      • 因为此时 \([l, r]\) 的编号本身就 \(> [ll[r], l]\),所以只要值 \(\ge [ll[r],l]\) 就可以
      • 而上面 \([l, r]\) 的编号 $ < [r, rr[l]]$ ,如果值相等,那就会取后者而不是 \([l, r]\)
    • 前后缀和的 \(l, r\) 具体是谁

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int MOD = 1e9 + 7;
const int N = (int)5e3 + 10;

int n;
int s[N];
int dp[N][N], pre[N][N], suf[N][N];
int inv[N], ll[N], rr[N];

int Q_pow(int a, int b){
	int ans = 1, p = a;
	while(b){
		if(b & 1){
			ans = (ans * p) % MOD;
		}
		p = (p * p) % MOD;
		b >>= 1;
	}
	return ans;
}

signed main(){
	cin >> n;
	inv[0] = 1;
	for(int i = 1; i <= n; i++){
		cin >> s[i];
		s[i] += s[i - 1];
		inv[i] = Q_pow(i, MOD - 2);
		ll[i] = 1, rr[i] = n;
	}
	
	dp[1][n] = pre[1][n] = suf[1][n] = inv[n - 1];
	for(int i = n - 1; i >= 1; i--){
		for(int l = 1, r = i; r <= n; l++, r++){
			while(s[r] - s[l - 1] <= s[rr[l]] - s[r]){
				rr[l]--;
			}
			// l, r + 1
			(dp[l][r] += (suf[l][r + 1] - suf[l][rr[l] + 1] + MOD) % MOD) %= MOD;
			
			while(s[r] - s[l - 1] < s[l - 1] - s[ll[r] - 1]){	// 为什么不是 <= ! 
				ll[r]++;
			}
			// l - 1, r
			(dp[l][r] += (pre[l - 1][r] - pre[ll[r] - 1][r] + MOD) % MOD) %= MOD;
			
			
			(dp[l][r] *= inv[r - l]) %= MOD;
			suf[l][r] = suf[l][r + 1];
			(suf[l][r] += dp[l][r]) %= MOD;
			pre[l][r] = pre[l - 1][r];
			(pre[l][r] += dp[l][r]) %= MOD;
		}
	}
	for(int i = 1; i <= n; i++){
		cout << dp[i][i] << "\n";
	}
	cout << "\n";
}

标签:int,ll,P10141,ans,inv,dp,MOD
From: https://www.cnblogs.com/wangyangjena/p/18028342

相关文章