[CSP-S2019 江西] 多叉堆

[CSP-S2019 江西] 多叉堆

题意:

给定 \(n\) 个节点,初始独立,有 \(q\) 次操作:

  • 1,x,y 选择 \(x\) , \(y\),将以 \(x\) 为根的子树合并到 \(y\)
  • 2 x 选择节点 \(x\) ,求节点 \(x\) 所在的树成 小根堆 的概率

分析:

我看题解大部分都是用组合数意义做的,我这个是用概率方面的知识写的。

小根堆:

什么情况下一棵树会形成小根堆?当然是每个点作为根节点时,其儿子节点的值大于这个点。

假如说这棵树只有一层,树的大小为 \(x\) ,每个节点赋值 \([1,x]\) ,形成小根堆的概率就是 \(\frac{1}{size[x]}\)

所以,我们可以拓展到更加复杂的树:

设一棵树为 \(E\) ,将这棵树每个节点赋值 \([1,size[E]]\) ,其形成小根堆概率为:

\[P(E)=\prod_{x \in E}\frac{1}{size[x]} \]

这些数一共有 \(size[E]!\) 次填法,因此产生的小根堆次数为:

\[size[E]! \times P(E) \]

因此,我们记录一下每个节点对应的根对应的形成小根堆的概率 \(P\),以及这个树的大小 \(size\)

询问时,利用 并查集 搜索这个节点对应的根,运用公式输出即可。

但是,我并没有讲怎么合并两个树,接下来就是合并的部分:

合并两棵树:

先用一张图:

此时,\(x\) 要合并到 \(y\) 上,利用并查集合并。

子树大小好弄,也就是

size[y]+=size[x];

但是怎么合并概率呢?

看树的大小:此时,只有以 \(y\) 为根对应的子树大小改变了,而其他都没有改变。

因此,我们要去除 \(\frac{1}{size[y]}\) 的贡献,增加 \(\frac{1}{size[x]+size[y]}\) 的贡献。

同时,还要增加树 \(x\) 的贡献.

因为不能用除法,因此需要用到 逆元,根据 求逆元 的相关知识,去除一个数在逆元中的贡献,等于乘这个数的倒数

合并可以写成以下式子:

P[y]=P[y]*P[x]*size[y]*inv[sizes[x]+sizes[y]];

其中,\(inv\) 为逆元。

这样,我们把子树 \(x\) 父亲设成 \(y\) ,合并 概率 \(P\)大小 \(size\) , 查询时利用公式输出即可。

代码:

// P5689 [CSP-S2019 江西] 多叉堆

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=6e5+5,mod=1e9+7;
int n,q;
int fa[N],sizes[N],ans;
int inv[N],mul[N],fac[N],now;
int P[N];


void init(int n){
    inv[1]=mul[1]=1;
    for(int i=2;i<=n;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    for(int i=2;i<=n;i++) mul[i]=(mul[i-1]*i)%mod;
}

int get(int x){
    if(x==fa[x]) return x;
    else return fa[x]=get(fa[x]);
}

signed main(){
    cin>>n>>q;
    for(int i=0;i<n;i++) fa[i]=i,sizes[i]=1,P[i]=1;
    init(300000);
    while(q--){
        int op,x,y; scanf("%lld",&op);
        if(op==1){
            scanf("%lld%lld",&x,&y); x=(x+ans)%n,y=(y+ans)%n;
            int fx=get(x),fy=get(y);
            P[fy]=P[fy]*P[fx]%mod*sizes[fy]%mod*inv[sizes[fx]+sizes[fy]]%mod;
            fa[fx]=fy;
            sizes[fy]+=sizes[fx];
        }
        else{
            scanf("%lld",&x); x=(x+ans)%n;
            int now=get(x);
            ans=P[now]*mul[sizes[now]]%mod;
            printf("%lld\n",ans);
        }
    }
    system("pause");
    return 0;
}

posted @ 2021-10-13 17:31  Evitagen  阅读(83)  评论(0编辑  收藏  举报