UNR #3 百鸽笼

Posted weiyanpeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UNR #3 百鸽笼相关的知识,希望对你有一定的参考价值。

题目大意:在UOJ管理员群里一共有\(N\)个管理员,为了容纳这些管理员,vfk准备了\(N+1\)个鸽笼。

为了节省空间,vfk把这些鸽笼堆了起来,共有\(n\)列,第i列放了\(a_i\)个鸽笼,满足 \(\sum a_i=N+1\)

每当UR结束,管理员们就会按照编号从小到大的顺序回到鸽笼里,每个管理员回来的时候,会先等概率的在所有还有剩余的鸽笼的列中随机一个列,然后住到这列剩下的鸽笼里编号最小的一个中。

现在\(N\)个管理员都回笼了之后,还有一列会空出一个鸽笼。你能对于每一列,求出这一列有空鸽笼的概率吗?

\(a_i \le 30, N\le 30\)

PKUWC2018猎人杀很像。

不必要的一步,问题可以转化为无限选直到只剩一个\(a_i>0\)

对于每个点,枚举一个集合\(S\),算出这个点在这个集合\(S\)所有点之前被删完的概率。显然集合外的操作我们可以忽略。集合内部的操作我们可以忽略不计。

考虑这个点被选完的时候的操作序列,这个东西可以用一个背包做出来。于是有一个\(2^n * somthing\)的复杂度的东西。我们再加一维表示当前的集合大小,就行了。

瞎写一发,发现复杂度达到了\(O(n^4m^2)\),T了。我们把那个背包做完然后撤销就行了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
inline int add(int a,int b)a+=b;return a>=mod?a-mod:a;
inline int sub(int a,int b)a-=b;return a<0?a+mod:a;
inline int mul(int a,int b)return (ll)a*b%mod;
inline int qpow(int a,int b)int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;
inline int inv(int x)return qpow(x,mod-2);
/* math */
const int N = 40, SZ = 910;
int n,a[N];
int binom[2010][2010];
inline void preset(int n=2000)
    binom[0][0]=1;
    for(int i=1;i<=n;i++)
        binom[i][0]=binom[i][i]=1;
        for(int j=1;j<i;j++)binom[i][j]=add(binom[i-1][j-1],binom[i-1][j]);
    

int f[N][SZ];
int g[N][SZ];

int sum = 0;
inline void package(int sz)
    for(int i=1;i<=n;i++)
        for(int j=0;j<=sum;++j)
            g[i][j]=f[i][j];
            for(int d=0;d<sz&&j-d>=0;d++)
                g[i][j]=add(g[i][j],mul(f[i-1][j-d],binom[j][d]));
            
        
    
    for(int i=1;i<=n;i++)
        for(int j=0;j<=sum;++j)
            f[i][j]=g[i][j];
        
    


inline void unpackage(int sz)
    for(int i=1;i<=n;i++)
        for(int j=0;j<=sum;++j)
            g[i][j]=f[i][j];
            for(int d=0;d<sz&&j-d>=0;d++)
                g[i][j]=sub(g[i][j],mul(f[i-1][j-d],binom[j][d]));
            
            f[i][j]=g[i][j];
        
    



int main()

    preset();
    cin >> n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];
    f[0][0]=1;
    for(int i=1;i<=n;i++)package(a[i]);
    for(int i=1;i<=n;i++)
        unpackage(a[i]);
        int ans=0;
        for(int S=1;S<=n;++S)
            for(int len=0;len<=sum;++len)
                int totlen = len+a[i], way = mul(f[S-1][len], binom[len+a[i]-1][a[i]-1]);
                int p=qpow(inv(S),totlen);
                if(S&1) ans=add(ans, mul(p,way));
                else ans=sub(ans, mul(p,way));
            
        
        package(a[i]);
        printf("%d ",ans);
    
    puts("");

以上是关于UNR #3 百鸽笼的主要内容,如果未能解决你的问题,请参考以下文章

[UNR #3]百鸽笼

[UNR #3]百鸽笼

UOJ390UNR #3百鸽笼容斥,EGF

Gym100851G Generators 思维 (鸽笼原理)

UOJ#386UNR#3鸽子固定器(贪心)

[UOJ388]UNR #3配对树