CF1295F

题目

(a_i)在范围([l_i, r_i])等概率取值,求序列({a_i})非递增的概率。(nle 50)(l_ile r_i le 998244351)

题解

即求序列非递增的方案数。

由于区间范围非常大,而区间数又很少。因此可以离散化端点,给区间分段。这样分下来总段数就是(O(n))级别了。可以使用dp解决。

这样每个大区间,每分成若干个连续的右闭左开的小区间,以及最后一个左闭右闭仅仅包含一个数的小区间。

(dp[i][j])代表前(i)个区间,当前在第(j)个小区间的方案数。转移很好想,关键在于求解一个区间内(k)个非递增位置的方案数。这个用差分的方法,转化为差值大于等于0,差值求和小于等于区间长度,可以用隔板法解决。

注意最后左闭右闭小区间的处理即可。

#include <bits/stdc++.h>

#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e2 + 10;
const int MX = 1e5 + 10;
const int M = 998244353;
const double eps = 1e-5;

ll dp[N][N];
ll rfact[MX];
typedef pair<int, int> PII;
PII segs[N];
vector<int> num;


inline ll qpow(ll a, ll b, ll m) {
	ll res = 1;
	while(b) {
		if(b & 1) res = (res * a) % m;
		a = (a * a) % m;
		b = b >> 1;
	}
	return res;
}

int main() {
	IOS;
	rfact[0] = 1;
	for(int i = 1; i < MX; i++) {
		rfact[i] = rfact[i - 1] * qpow(i, M - 2, M) % M;
	}
	num.push_back(0);
	int n;
	cin >> n;
	ll tot = 1;
	for(int i = 1; i <= n; i++) {
		int l, r;
		cin >> l >> r;
		tot = (r - l + 1) * tot % M;
		segs[i] = {l, r};
		num.push_back(l);
		num.push_back(r);
	}
	sort(num.begin(), num.end());
	num.erase(unique(num.begin(), num.end()), num.end());
	for(int i = 1; i <= n; i++) {
		segs[i].first = lower_bound(num.begin(), num.end(), segs[i].first) - num.begin();
		segs[i].second = lower_bound(num.begin(), num.end(), segs[i].second) - num.begin();
	}
	for(int i = 0; i < N; i++) dp[0][i] = 1;
	for(int i = 1; i <= n; i++) {
		int l = segs[i].first, r = segs[i].second;
		for(int j = r; j >= l; j--) {
			int rg;
			if(j - 1 >= l) rg = num[j] - num[j - 1];
			else rg = 1;
			ll pw = rg;
			for(int k = i - 1; k >= 0; k--) {

				if(k && segs[k].first == j) 
					dp[i][j] = (dp[i][j] + dp[k][j] % M * pw % M * rfact[i - k] % M) % M;
				else
					dp[i][j] = (dp[i][j] + dp[k][j + 1] % M * pw % M * rfact[i - k] % M) % M;
				pw = pw * (rg + i - k) % M;
				if(!(segs[k].second >= j && segs[k].first < j)) break;
			}
			dp[i][j] = (dp[i][j] + dp[i][j + 1]) % M;
		}
		for(int j = l - 1; j >= 0; j--) {
			dp[i][j] = dp[i][j + 1];
		}
	}
	cout << dp[n][0] * qpow(tot, M - 2, M) % M << endl;
}
原文地址:https://www.cnblogs.com/limil/p/15267773.html