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
原文地址:https://www.cnblogs.com/farway17/p/15063481.html