zoukankan      html  css  js  c++  java
  • CF1295F Good Contest

    传送门


    题意:\(a_i\)\([l_i,r_i]\)等概率取,问\(a_{1\cdots n}\)不递增的概率,对\(998244353\)取模。(\(1 \leqslant n \leqslant 50, 1 \leqslant l_i \leqslant r_i < 998244353\)


    可以先求方案数,然后除以\(\prod\limits_{i=1}^n (r_i-l_i+1)\).如果直接令\(dp[i][j]\)表示第\(i\)个数选\(j\)时的方案数,显然会超时。因为\(n\)个区间最多会划分出\(2n-1\)个不相交的区间,考虑到\(n\)很小,可以从这方面入手。

    先把所有区间右端点+1,处理成\(2n-1\)个左闭右开的区间。令\(dp[i][j]\)表示第\(i\)个数选择第\(j\)个区间中的某个数时合法序列的方案数。那么只要枚举他和前面多少个数选择同一个区间\(j\)即可。那么从区间\(I\)中选择\(a\)个数,使其构成不递增序列的方案数,实际上就是隔板法,而隔板法的含义就是从\(I\)中随便选出\(a\)个数的方案数,即\(\binom{|I|+a-1}{a}\),那么就有dp转移方程:

    \[dp[i][j] = \sum\limits_{k < i, t > j} dp[k][t] * \binom{|I_j|+i-k-1}{i - k} \]

    关于组合数,虽然\(|I_j|\)会很大,单次需要\(O(n)\)算,但是从\(k\)转移到\(k-1\)是有规律的,可以动态求。所以这样转移时间复杂度是\(O(n^4 \log n)\)的(有个快速幂的\(\log n\),应该也可以优化),再用后缀和可以优化到\(O(n^3\log n)\).

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 55;
    const ll mod = 998244353;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    In ll ADD(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;}
    In ll quickpow(ll a, ll b)
    {
    	ll ret = 1;
    	for(; b; b >>= 1, a = a * a % mod)
    		if(b & 1) ret = ret * a % mod;
    	return ret;
    }
    
    int n;
    struct Node {int L, R;}t[maxn];
    int li[maxn << 1], _n = 0;
    ll dp[maxn][maxn << 1], sum[maxn][maxn << 1];
    
    int main()
    {
    	n = read();
    	for(int i = 1; i <= n; ++i)
    	{
    		int L = read(), R = read() + 1;
    		li[++_n] = L, li[++_n] = R;
    		t[i] = (Node){L, R}; 
    	}
    	sort(li + 1, li + _n + 1);
    	_n = unique(li + 1, li + _n + 1) - li - 1;
    	fill_n(sum[0], _n + 1, 1);
    	t[0].L = 0, t[0].R = _n;
    	for(int i = 1; i <= n; ++i)
    	{
    		t[i].L = lower_bound(li + 1, li + _n + 1, t[i].L) - li;
    		t[i].R = lower_bound(li + 1, li + _n + 1, t[i].R) - li;
    		for(int j = t[i].L; j < t[i].R; ++j)
    		{
    			ll c = li[j + 1] - li[j]; ll len = c;
    			for(int k = i - 1; k >= 0; --k) 
    			{ 
    				if(t[k + 1].L > j || t[k + 1].R <= j) break;	//第k+1个区间可能不包含当前枚举的区间 
    				dp[i][j] = ADD(dp[i][j], sum[k][j + 1] * c % mod);
    				c = c * (len + i - k) % mod * quickpow(i - k + 1, mod - 2) % mod;
    			} 
    		}
    		for(int j = _n; j; --j) sum[i][j] = ADD(sum[i][j + 1], dp[i][j]);
    	}
    	ll ans = sum[n][1];
    	for(int i = 1; i <= n; ++i) ans = ans * quickpow(li[t[i].R] - li[t[i].L], mod - 2) % mod;
    	write(ans), enter;
    	return 0;
    }
    
  • 相关阅读:
    新院址运行统计
    游标使用之四
    游标使用之三
    css基础知识
    javascript基础知识
    [每日一题2020.06.20]BFS
    白嫖一个免费域名并解析到博客园
    [每日一题2020.06.19]leetcode #84 #121 单调栈
    操作系统---文件管理
    [每日一题2020.06.18]leetcode #3 hash_map实现滑动窗口
  • 原文地址:https://www.cnblogs.com/mrclr/p/15599201.html
Copyright © 2011-2022 走看看