• [Luogu#4707] 重返现世(minmax容斥+背包dp)


    Address

    Luogu#4707

    Solution

    前置技能:记 \(max_k(S)\) 表示 \(S\) 中第 \(k\) 大的数,\(min(S)\) 表示 \(S\) 中最小的数,那么有:$$max_k(S)=\sum_{T∈S,T\neq\emptyset}\binom{|T|-1}{k-1}(-1)^{|T|-k}min(T)$$证明:我们考虑构造容斥系数,即设:

    \[max_k(S)=\sum_{T∈S,T\neq\emptyset}f(|T|)min(T) \]

    \(min(T)=min_x(S)\) 时:

    \[\sum f(|T|)=\sum_{i=0}^{n-x}\binom{n-x}{i}f(i+1) \]

    那么有:$$\sum_{i=0}^{n-x}\binom{n-x}{i}f(i+1)=[x=n-k+1]=[n-x=k-1]$$

    二项式反演可得:

    \[f(a+1)=\sum_{i=0}^a\binom{a}{i}(-1)^{a-i}[i=k-1] \]

    因此:

    \[f(a)=\binom{a-1}{k-1}(-1)^{a-k} \]

    即:

    \[f(|T|)=\binom{|T|-1}{k-1}(-1)^{|T|-k} \]

    \(\text{min-max}\)容斥可以扩展到期望上来,即 $$E(max_k(S))=\sum_{T∈S,T\neq\emptyset}\binom{|T|-1}{k-1}(-1)^{|T|-k}E(min(T))$$

    接下来考虑对于一个集合 \(T\),怎样计算 \(E(min(T))\)

    \(P(T)=\sum_{i∈T}p_i\),那么把 \(T\) 集合内的物品看作一个整体,其它物品也看作一个整体。问题转化为:有两个物品,每次取到 \(1\) 号的概率为 \(\frac{P(T)}{m}\),取到 \(2\) 号的概率为 \(1-\frac{P(T)}{m}\),问取到 \(1\) 号的期望次数。

    设取到 \(1\) 号的期望次数为 \(x\),则 \(x=\frac{P(T)}{m}+(1-\frac{P(T)}{m})(x+1)\),解得 \(x=\frac{m}{P(T)}\)

    注意题目中的 \(k\) 指的是 \(min_k(S)\),令 \(k=n-k+1\),变为求 \(E(max_k(S))\)

    如果对于每个 \(P(T)=j\) 的集合 \(T\),算出它们的 \(\binom{|T|-1}{k-1}(-1)^{|T|-k}\) 之和,就能求出答案了。

    考虑 \(\text{dp}\),记 \(f[i][j][k]\) 表示考虑前 \(i\) 个数的子集 \(T\)\(P(T)=j\),它们的 \(\binom{|T|-1}{k-1}(-1)^{|T|-k}\) 之和,初值 \(f[0][0][0]=1\)

    转移:

    1. \(T\) 中没有 \(i\),那么 \(f[i][j][k]+=f[i-1][j][k]\)
    2. \(T\) 中有 \(i\),那么我们先把 \(T\) 中的 \(i\) 移除,然后 \(T\) 变成了前 \(i-1\) 个数的子集:

    \[\sum_{T}\binom{|T|}{k-1}(-1)^{|T|+1-k} \]

    \[=\sum_T[\binom{|T|-1}{k-1}+\binom{|T|-1}{k-2}](-1)^{T-k+1} \]

    \[=\sum_T\binom{|T|-1}{k-1}(-1)^{T-k}(-1)+\sum_T\binom{|T|-1}{(k-1)-1}(-1)^{|T|-(k-1)} \]

    \[=f[i-1][j-p_i][k-1]-f[i-1][j-p_i][k] \]

    综上所述,\(f[i][j][k]=f[i-1][j][k]+(f[i-1][j-p_i][k-1]-f[i-1][j-p_i][k])[k\geq 1]\)

    时间复杂度 \(O(n(n-k)m)\),注意第一维要滚动。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    template <class t>
    inline void read(t & res)
    {
    	char ch;
    	while (ch = getchar(), !isdigit(ch));
    	res = ch ^ 48;
    	while (ch = getchar(), isdigit(ch))
    	res = res * 10 + (ch ^ 48);
    }
    
    const int e = 10005, mod = 998244353;
    int p[e], f[2][e][15], inv[e], n, q, m, ans;
    
    inline void add(int &x, int y)
    {
    	(x += y) >= mod && (x -= mod);
    }
    
    int main()
    {
    	int i, j, k, s = 0;
    	read(n); read(q); read(m); q = n - q + 1;
    	f[0][0][0] = 1; inv[1] = 1;
    	for (i = 2; i <= m; i++) inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
    	for (i = 1; i <= n; i++) read(p[i]);
    	for (i = 1; i <= n; i++)
    	{
    		int nxt = i & 1, lst = nxt ^ 1;
    		memset(f[nxt], 0, sizeof(f[nxt]));
    		s += p[i];
    		for (j = 0; j <= s; j++)
    		for (k = 0; k <= q; k++)
    		{
    			f[nxt][j][k] = f[lst][j][k];
    			if (j >= p[i] && k) 
    			{
    				add(f[nxt][j][k], f[lst][j - p[i]][k - 1]);
    				add(f[nxt][j][k], mod - f[lst][j - p[i]][k]);
    			}
    		}
    	}
    	for (i = 1; i <= m; i++) ans = (ans + (ll)f[n & 1][i][q] * inv[i]) % mod;
    	ans = (ll)ans * m % mod;
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    【NET】File操作练习笔记
    【微信小程序】分包的使用和预下载
    【微信小程序】组件Component常用案例
    MVCC多版本并发控制
    数据存储引擎
    seata 分布式事务 -- seata-three工程完整代码
    seata 分布式事务 -- seata-two工程完整代码
    seata 分布式事务 -- seata-one 工程完整代码
    seata 分布式事务 -- 准备工作
    seata 分布式事务 -- TCC模式
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12199217.html
Copyright © 2020-2023  润新知