• 2021 牛客多校 第四场 选做


    Link(需报名看题)

    比赛小记

    我过了几个签到,牛逼题(B、C、E)都是学长过的(orz学长)

    J 莫名其妙(因为精度?)wa了好几发,这把只贡献罚时了属于是

    B. Sample Game

    题意

    (n) 个数和一个空数列,每次向数列中随机添加一个数,添加数 (i) 的概率为 (p_i),当数列不是单调不降时停止操作。求数列长度的平方的期望。

    (2le nle 100)

    Solution

    orz学长

    类比 bzoj osu! 的做法,我们可以考虑把 (x^2) 拆开,令 (f_{i,0/1/2}) 分别表示到 (i) 的概率;到 (i) 的长度的期望和到 (i)​ 的长度平方的期望。可得:

    [egin{align*} f_{i,0}&= p_isum_{j=0}^{i}f_{j,0}\ f_{i,1}&=p_isum_{j=0}^{i}(f_{j,1}+f_{j,0})\ f_{j,2}&=p_isum_{j=0}^{i}(f_{j,2}+2f_{j,1}+f_{j,0}) end{align*} ]

    移项一下即可得到递推式,统计答案相当于再加上一个 (lt i) 的点,直接做即可。

    code

    (by 学长)

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
     
    #define Rep(i, n) for (int i = 1; i <= n; i ++)
    #define Rep0(i, n) for (int i = 0; i <= n; i ++)
    #define RepG(i, x) for (int i = head[x]; i; i = edge[i].to)
    #define v edge[i].to
    #define mp(a, b) make_pair(a, b)
    #define fi first
    #define se second
     
    typedef double D;
    typedef long long LL;
     
    const int mod = 998244353;
     
    LL p[110], f[110][3];
    LL qpow(LL a, LL b)
    {
        LL ret = 1, base = a;
        while (b) {
            if (b & 1) ret = ret * base % mod;
            base = base * base % mod, b >>= 1;
        }
        return ret;
    }
     
    int main()
    {
        int n, sum = 0;
        scanf("%d", &n);
        Rep(i, n) { scanf("%d", &p[i]); sum += p[i];}
        sum = qpow(sum, mod - 2);
        Rep(i, n) p[i] = p[i] * sum % mod;
         
        LL s0 = 1, s1 = 0, s2 = 0;
        f[0][0] = 1;
        Rep(i, n) {
            LL tmp = qpow(1 - p[i] + mod, mod - 2);
            f[i][0] = s0 * p[i] % mod * tmp % mod;
            s0 = (s0 + f[i][0]) % mod;
            f[i][1] = (s0 + s1) * p[i] % mod * tmp % mod;
            s1 = (s1 + f[i][1]) % mod;
            f[i][2] = (s2 + s0 + s1 * 2) * p[i] % mod * tmp % mod;
            s2 = (s2 + f[i][2]) % mod;
        }
        LL sp = 0, ans = 0;
        Rep(i, n) {
            LL tmp = sp * (f[i][2] + f[i][0] + f[i][1] * 2) % mod;
            ans = (ans + tmp) % mod, sp = (sp + p[i]) % mod;
        }
         
        printf("%lld
    ", ans);
         
        return 0;
    }
    

    E. Tree Xor

    Solution

    显然题意可以转化成求满足 (x_i~mathrm{xor}~w'_iin[l_i,r_i])​ 的 (x)​ 的个数(这里 (w_i)​​ 为当前点到根的 (w_i) 异或和)。

    考虑 ([l_i,r_i])​​ 一定能拆分成 ( ext{trie})​​ 树上 (log) 段区间,而 ( ext{xor})​ 操作相当于交换 ( ext{trie}) 树上若干子树,不会影响区间数量。我们对于异或可以在根节点打上标记,然后将 ([l_i,r_i]) 直接插入 ( ext{trie}) 树即可。

    code

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi()
    {
        char c=getchar(); int x=0;
        for(;c<'0'||c>'9';c=getchar());
        for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
        return x;
    }
    const int N=3e5+5;
    int n,l[N],r[N],w[N],x[N],ch[N*30][2],tag[N*30],add[N*30],tot=1,ans;
    vector<pair<int,int>> e[N];
    void dfs(int u, int fa)
    {
        for(auto x:e[u]) if(x.first!=fa)
        {
            w[x.first]=w[u]^x.second;
            dfs(x.first,u);
        }
    }
    void pushdown(int u, int i, bool f)
    {
        if(!ch[u][0]&&f) ch[u][0]=++tot;
        if(!ch[u][1]&&f) ch[u][1]=++tot;
        if(!tag[u]) return ;
        if(tag[u]&(1<<i)) swap(ch[u][0],ch[u][1]);
        tag[ch[u][0]]^=tag[u],tag[ch[u][1]]^=tag[u];
        tag[u]=0;
    }
    void trie_ins(int l, int r, int u=1, int x=0, int i=29)
    {
        if(i<0)
        {
            ++add[u];
            return ;
        }
        pushdown(u,i,1);
        if(l<=x&&x+(1<<i+1)-1<=r)
        {
           ++add[u];
           return ;
        }
        if(x+(1<<i)-1>=l) trie_ins(l,r,ch[u][0],x,i-1);
        if(x+(1<<i)<=r) trie_ins(l,r,ch[u][1],x|(1<<i),i-1);
    }
    void trie_dfs(int u=1, int sum=0, int i=29)
    {
        if(!u) return ;
        pushdown(u,i,0);
        sum+=add[u];
        if(sum==n)
        {
            ans+=(1<<i+1);
            return ;
        }
        if(i<0) return ;
        trie_dfs(ch[u][0],sum,i-1);
        trie_dfs(ch[u][1],sum,i-1);
    }
    int main()
    {
        n=gi();
        for(int i=1;i<=n;++i) l[i]=gi(),r[i]=gi();
        for(int i=1;i<n;++i)
        {
            int u=gi(),v=gi(),w=gi();
            e[u].push_back(make_pair(v,w)),e[v].push_back(make_pair(u,w));
        }
        dfs(1,0);
        for(int i=1;i<=n;++i)
        {
            tag[1]^=w[i];
            trie_ins(l[i],r[i]);
            tag[1]^=w[i];
        }
        trie_dfs();
        printf("%d
    ",ans);
    }
    

    G. Product

    题意

    给定 (n,k,D),定义数列 ({a_n}) 的价值为

    [frac{D!}{prod_{i=1}^n(a_i+k)!} ]

    求所有满足以下条件的数列 ({a_n}) 的价值之和:

    1. (forall iin[1,n],a_ige 0)
    2. (sum_{i=1}^n a_i=D)

    (998244353)​ 取模。

    (1le nle 50)(0le kle 50)(0le Dle 10^8)

    Solution

    考虑 (k=0)​​ 下该式的组合意义,考虑有一个长度为 (D)​​ 的序列,而 (a_i)​​ 为 (i)​​ 出现的次数,那么 (sum_{a_ige 0,sum a_i=D}frac{D!}{prod_{i=1}^n a_i!})​​ 即为值域为 ([1,n])​​ 的长度为 (D)​​​ 的数列个数(缩排),即为 (n^D)​。

    我们直接令 (a_i) 加上 (k) ,题意转化为求 (sum_{a_ige k,sum a_i=D+nk}frac{D!}{prod_{i=1}^n a_i!})。若无 (a_ige k) 的限制,答案即为 (frac{D!}{(D+nk)!}n^{D+nk})。对于限制,我们考虑容斥:令 (x)(a_ilt k) ,这些 (frac{1}{a_i!}) 的贡献可以用 (O(n^2k^2)) 的背包求出;剩余 (n-x) 个数的贡献即为 (frac{D!}{(D+nk-s)!}n^{D+nk-s})(s) 为 打破限制的 (x) 个数之和),乘起来再乘上容斥系数即可。

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=55,M=N*N,Mod=998244353;
    int n,k,d,f[N][M],fac[M],inv[M],ans;
    #define mul(x,y) (1ll*(x)*(y)%Mod)
    inline int add(int x, int y)
    {   return (x+y>=Mod?x+y-Mod:x+y);
    }
    inline int sub(int x, int y)
    {   return (x-y<0?x-y+Mod:x-y);
    }
    inline void upd(int& x, int y)
    {   x=(x+y>=Mod?x+y-Mod:x+y);
    }
    inline int po(int x, int y)
    {
        int r=1;
        while(y)
        {
            if(y&1) r=mul(r,x);
            x=mul(x,x), y>>=1;
        }
        return r;
    }
    inline int C(int x, int y)
    {
        return mul(fac[x],mul(inv[y],inv[x-y]));
    }
    int calcd(int delta)
    {
        int r=1;
        for(int i=d+1;i<=d+delta;++i) r=mul(r,i);
        return po(r,Mod-2);
    }
    int main()
    {
        scanf("%d%d%d",&n,&k,&d);
        fac[0]=inv[0]=1;
        const int m=max(n,n*(k-1));
        for(int i=1;i<=m;++i) fac[i]=mul(fac[i-1],i);
        inv[m]=po(fac[m],Mod-2);
        for(int i=m-1;i>0;--i) inv[i]=mul(inv[i+1],i+1);
        f[0][0]=1;
        for(int i=1;i<=n;++i)
            for(int j=0;j<=(i-1)*(k-1);++j)
                for(int l=0;l<k;++l)
                        upd(f[i][j+l],mul(f[i-1][j],inv[l]));
        for(int i=0;i<=n;++i)
        {
            for(int j=0;j<=i*(k-1);++j)
            {
                const int delta=n*k-j;
                int tmp=mul(f[i][j],mul(po(n-i,d+delta),calcd(delta)));
                if(i&1) ans=sub(ans,mul(tmp,C(n,i)));
                else ans=add(ans,mul(tmp,C(n,i)));
            }
        }
        printf("%d
    ",ans);
    }
    
    --- sto rushcheyo orz
  • 相关阅读:
    【JLOI2011】飞行路线
    P3369 【模板】普通平衡树
    P1144 最短路计数
    P1462 通往奥格瑞玛的道路
    【NOIP2017】宝藏
    P1120 小木棍
    P3919 【模板】可持久化数组(可持久化线段树/平衡树)
    P3834 【模板】可持久化线段树 1(主席树)
    矩阵清零--进军硅谷
    二维数组搜素--进军硅谷
  • 原文地址:https://www.cnblogs.com/farway17/p/15063481.html
Copyright © 2020-2023  润新知