【题解】[SHOI2002]百事世界杯之旅

$\text{min-max}$ 容斥!

题目链接:[SHOI2002]百事世界杯之旅

题意:

有 $n$ 个物品,每次等概率选择一个(一个物品可重复选择),求得到所有物品的期望次数。

$2\le n\le 33$。

看起来这题有一个很简单的做法,但是本人第一眼就是 $\text{min-max}$ 容斥啊......

众所周知:

$$\max(S)=\sum_{T\subseteq S} (-1)^{|T|-1}\min(T)$$

需要注意的是这个柿子套上期望也是成立的,即:

$$E(\max(S))=\sum_{T\subseteq S} (-1)^{|T|-1}E(\min(T))$$

这里不妨直接设 $\min(S)$ 为得到 $S$ 中任意元素的最少次数,$\max(S)$ 为得到 $S$ 中所有元素的次数。

那么显然 $E(\min(S))=\dfrac{n}{|S|}$,因为一次操作选中一个 $S$ 中元素的概率的概率是 $\dfrac{|S|}{n}$,期望就是其倒数。

那么可以把答案写成:

$$\text{Ans}=\sum_{S}(-1)^{|S|-1} \dfrac{n}{|S|}$$

发现柿子只和 $S$ 的大小有关了,转变为枚举 $|S|$。显然的,大小为 $x$ 的集合个数是 $\binom{n}{x}$。

枚举 $|S|$,答案可以写成:

$$\text{Ans}=\sum_{i=1}^n (-1)^{i-1}\times \dfrac{n}{i}\times \binom{n}{i}$$

组合数只要求一行,直接 $\mathcal{O}(n)$ 递推,然后就可以按上述柿子模拟。

//Code By CXY07
#include<bits/stdc++.h>
using namespace std;

//#define FILE
#define int long long
#define debug(x) cout << #x << " = " << x << endl
#define file(FILENAME) freopen(FILENAME".in", "r", stdin), freopen(FILENAME".out", "w", stdout)
#define LINE() cout << "LINE = " << __LINE__ << endl
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fst first
#define scd second
#define inv(x) qpow((x),mod - 2)
#define lowbit(x) ((x) & (-(x)))
#define abs(x) ((x) < 0 ? (-(x)) : (x))
#define randint(l, r) (rand() % ((r) - (l) + 1) + (l))
#define vec vector

const int MAXN = 110;
const int INF = 2e9;
const double PI = acos(-1);
const double eps = 1e-6;
//const int mod = 1e9 + 7;
//const int mod = 998244353;
//const int G = 3;
//const int base = 131;

int Gcd(int x, int y) {
    if(!y) return x;
    return Gcd(y, x % y);
}

struct Frac {
    int a, b;
    Frac(int _a = 0, int _b = 0) : a(_a), b(_b) {}
    Frac operator + (const Frac &x) const {
        return Frac(a * x.b + b * x.a, x.b * b);
    }
    void clear() {
        static int gcd; gcd = Gcd(a, b);
        a /= gcd, b /= gcd;
    }
} Ans(0, 1);

int n, m, C;

template<typename T> inline bool read(T &a) {
    a = 0; char c = getchar(); int f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {a = a * 10 + (c ^ 48); c = getchar();}
    a *= f;
    return 1;
}

template<typename A, typename ...B>
inline bool read(A &x, B &...y) {return read(x) && read(y...);}

int count(int x) {
    int res = 0;
    for(; x; x /= 10, res++);
    return res;
}

signed main () {
    read(n); C = 1;
    for(int i = 1, opt = 1; i <= n; ++i) {
        C = C * (n - i + 1) / i;
        Frac now = Frac(opt * n * C, i);
        now.clear();
        Ans = Ans + now; Ans.clear();
        opt *= -1;
    }
    Ans.clear();
    int x = Ans.a / Ans.b, y = Ans.a % Ans.b, z = Ans.b;
    if(z == 1) return printf("%lld\n", x), 0;
    if(!x) {
        int tmp = max(count(y), count(z));
        printf("%lld\n", y);
        for(int i = 1; i <= tmp; ++i) putchar('-');
        printf("\n%lld\n", z);
    } else {
        int tmp1 = max(count(y), count(z)), tmp2 = count(x);
        for(int i = 1; i <= tmp2; ++i) putchar(' ');
        printf("%lld\n%lld", y, x);
        for(int i = 1; i <= tmp1; ++i) putchar('-');
        printf("\n");
        for(int i = 1; i <= tmp2; ++i) putchar(' ');
        printf("%lld\n", z);
    }
    return 0;
}
添加新评论