ZR19CSP-S赛前冲刺day8
为了保护ZR的版权,这里不提供题目QWQ
http://zhengruioi.com/contest/449(你进得去吗/xyx)
A 闯关
没什么好说的,一眼题
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n, a[N], ans[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
int pos1 = 1, pos2 = n, lastt = n + 1;
for(int i = 1; i <= n; i ++) {// printf("%d %d\n", i, lastt);
ans[a[i]] = pos1 ++;
for(int j = lastt - 1; j > a[i]; j --) ans[j] = pos2 --;
int ha = a[i];
i += lastt - 1 - a[i];
lastt = ha;
}
for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
return 0;
}
B 动物园
发现对于
i
,
j
i, j
i,j两个动物如果还没有合并,那么他们存活的概率是独立的,即互不影响,然后如果想要把
i
,
j
i,j
i,j合并,那么,作为各种动物的概率也是相对相等的,所以就不用管他们具体是什么动物,
假设把
i
,
j
i,j
i,j合并,那
i
i
i作为主场,
i
i
i笼子里蠢货的概率就是
2
/
3
2/3
2/3,
j
j
j笼子里存活的概率就是
1
/
3
1/3
1/3。
考虑用并查集维护,节点
x
x
x表示以
x
x
x为根的子树的标记,
那就相当于再
i
i
i的根节点那里打一个
2
/
3
2/3
2/3的标记,然后在
j
j
j的根节点那里打上一个
1
/
3
1/3
1/3。
考虑如何路径压缩,发现就是一路上的标记(不包括根)的乘积再乘上当前点的标记,然后再连向根就可以了,
注意合并 i , j i, j i,j的时候 j j j要先除 i i i的标记,不然就多打了一次标记在 j j j上
概率算出来后直接乘 3 n 3^n 3n即可
code:
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 200005
using namespace std;
ll qpow(ll x, ll y) {
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int fa[N], n, m;
ll ha[N];
int get(int x) {
if(x == fa[x]) return x;
int f = get(fa[x]);
if(fa[x] != f) ha[x] = ha[x] * ha[fa[x]] % mod;//更新标记
return fa[x] = f;
}
int main() {
scanf("%d%d", &n, &m);
ll S = 1;
for(int i = 1; i <= n; i ++) fa[i] = i, ha[i] = 1, S = S * 3 % mod;
while(m --) {
int opt, x, y;
scanf("%d", &opt);
if(opt == 1) {
scanf("%d%d", &x, &y);
x = get(x), y = get(y);
fa[x] = y;
ha[x] = 2ll * ha[x] % mod * qpow(3ll, mod - 2) % mod;
ha[y] = ha[y] * qpow(3ll, mod - 2) % mod;
ha[x] = ha[x] * qpow(ha[y], mod - 2) % mod;
} else {
scanf("%d", &x);
int f = get(x);
ll ans = ha[x] * S % mod;
if(f != x) ans = ans * ha[f] % mod;
printf("%lld\n", ans);
}
}
return 0;
}
C 树
不会,先咕着