Winter Camp Day3 I

带权并查集

Posted by mental2008 on February 1, 2019

Description

题目链接: https://www.zhixincode.com/contest/12/problem/I?problem_id=191

题意是有一场 $n$ 个选手参加的石头剪刀布比赛,第 $i$ 个选手坐在第 $i$ 个位置,每个选手都有 $3$ 种可能情况

接下来有 $m$ 个询问,操作 $1$ $x$ $y$ 表示让 $y$ 位置的选手去挑战 $x$ 位置的选手,获胜的选手坐在 $x$ 位置,守擂方获胜概率为 $\frac{2}{3}$ ,攻擂方获胜概率为 $\frac{1}{3}$ ,操作 $2$ $x$ 表示询问初始位置为 $x$ 的选手仍未被淘汰的方案数

Solution

jls 所谓 very easy 的题目 QvQ

每个操作 $1$ 可以认为是 $y$ 向 $x$ 连一条有向边,其中攻擂方 $y$ 原先集合的所有元素的存活概率都乘上 $\frac{1}{3}$,而守擂方 $x$ 原先集合的所有元素的存活概率都乘上 $\frac{2}{3}$

每次查询的结果其实就是 $x$ 的存活概率乘上总的方案数 $3^n$

可以用带权并查集来实现这一过程

每次连边,都建一个新点,然后从 $y$ 连一条权值为 $\frac{1}{3}$ 的边,从 $x$ 连一条权值为 $\frac{2}{3}$ 的边

查询的过程等价于从结点 $x$ 向上走到当前根节点,答案就是路径上权值的乘积,路径可以在查询的过程中进行压缩

AC code

Time: 139ms

Memory: 8MB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<bits/stdc++.h>

using namespace std;

#define mem(a,i) memset(a,i,sizeof(a))
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define lowbit(x) (x&-x)
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll mod=998244353ll;
const int maxn=4e5+5;

int n,m;
ll val[maxn];
int fa[maxn];
ll a,b;
int cnt;

ll qpow(ll a,ll x) {
    ll res=1;
    ll p=a;
    while(x) {
        if(x&1) res=res*p%mod;
        p=p*p%mod;
        x>>=1;
    }
    return res;
}

ll find(int x) {
    if(fa[x]==x) return x;
    else {
        ll p=find(fa[x]);
        val[x]=val[fa[x]]*val[x]%mod;
        return fa[x]=p;
    }
}
void connect(int x,int y) {
    int p=++cnt;
    int fa_x=find(x);
    int fa_y=find(y);
    val[fa_x]=a;
    val[fa_y]=b;
    fa[fa_x]=p;
    fa[fa_y]=p;
}

int main() {
    scanf("%d%d",&n,&m);
    ll res=qpow(3ll,n);
    a=qpow(3ll,mod-2);
    b=a*2ll%mod;
    rep(i,0,maxn-1) {
        fa[i]=i;
        val[i]=1;
    }
    cnt=n;
    rep(i,1,m) {
        int o;
        scanf("%d",&o);
        if(o==1) {
            int x,y;
            scanf("%d%d",&x,&y);
            connect(y,x);
        }
        else {
            int x;
            scanf("%d",&x);
            find(x);
            ll ans=res*val[x]%mod;
            printf("%lld\n",ans);
        }
    }
    return 0;
}