鏈接:https://namomo.top:8081/contest/1/problem/B
來源:Namomo Test Round 1
思路:首先設第 個位置的概率爲 ,剛開始的時候 ,其他的都是 。當一個操作能夠被看到的時候,如果交換兩個位置,那麼兩個位置有兔子的概率將會被交換,也就是交換兩個 值,即 。當一個操作不能夠被看到的時候,此時就分爲兩種情況,第一種是當前的帽子不會被選中,當前帽子不被選中的概率爲
那麼當前位置有兔子的概率爲 ,另外一種情況就是當前的帽子被選中,按照上述條件可以知道被選中的概率爲 ,如果最終想要這個帽子有兔子,那麼選擇的另外一個帽子中必定有兔子,這樣才能保證交換以後當前帽子中有兔子,我們要在剩下 個帽子中選擇一個有兔子的帽子,另外一個帽子有兔子的概率爲
那麼在這種情況下當前帽子中有兔子的概率爲
綜上所述,當前帽子下有兔子的概率爲
+
我們在進行修改概率的時候,對於每一個帽子都需要進行修改,所以使用線段樹進行區間的修改,當是已知操作數進行概率交換的時候我們對線段樹的葉結點進行單點修改,線段樹中維護兩個值,一個是要乘的數 ,另外一個是要加的數 ,當我們在修改的時候可以下傳到某一個位置,如果滿足條件就不在進行下傳,保存當前的結點信息,在查詢的時候如果需要下傳就將信息下傳並且更新當前結點的信息,直到下傳到我的想要的值,在修改的時候,當往上傳的時候說明此時需要更新的點已經更新完畢,我們需要把每個結點的信息要乘上的數變爲 ,要加上的數變成 。
#include<bits/stdc++.h>
using namespace std;
#define LNode x << 1
#define RNode x << 1 | 1
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;
int x[maxn], y[maxn];
bool flag[maxn];
struct tree {
ll k, b, sum;
/*tree operator + (const tree & xxx) const {
tree ans;
ans.k = 1, ans.b = 0;
ans.sum = (sum + xxx.sum) % mod;
return ans;
}*/
}tree[maxn << 2];
ll quickPow(ll a, ll b) {
ll ans = 1, res = a;
while(b) {
if(b & 1) ans = ans * res % mod;
res = res * res % mod;
b >>= 1;
}
return ans % mod;
}
ll inv(ll x) {
return quickPow(x % mod, mod - 2);
}
void pushUp(int x) {
//tree[x] = tree[LNode] + tree[RNode];
tree[x].k = 1; tree[x].b = 0;
}
void pushDown(int x, ll k, ll b) {
tree[LNode].k = tree[LNode].k * k % mod;
tree[LNode].b = (tree[LNode].b * k % mod + b) % mod;
tree[LNode].sum = (tree[LNode].sum * k % mod + b) % mod;
tree[RNode].k = tree[RNode].k * k % mod;
tree[RNode].b = (tree[RNode].b * k % mod + b) % mod;
tree[RNode].sum = (tree[RNode].sum * k % mod + b) % mod;
tree[x].k = 1; tree[x].b = 0;
}
void build(int l, int r, int x) {
if(l == r) {
tree[x].sum = 0;
tree[x].k = 1;
tree[x].b = 0;
return ;
}
int mid = (l + r) >> 1;
build(l, mid, LNode);
build(mid+1, r, RNode);
pushUp(x);
}
//單點修改
void modify(int l, int r, int x, int pos, int val) {
if(l == r) {
tree[x].sum = val;
return ;
}
pushDown(x, tree[x].k, tree[x].b);
int mid = (l + r) >> 1;
if(pos <= mid) modify(l, mid, LNode, pos, val);
else modify(mid+1, r, RNode, pos, val);
pushUp(x);
}
//區間查詢
/*
int query(int l, int r, int L, int R, int x) {
if(L <= l && R >= r) return sum[x];
pushDown(x);
int mid = (l + r) >> 1, ans = 0;
if(L <= mid) ans += query(l, mid, L, R, x);
else ans += query(mid+1, r, L, R, x);
return ans;
} */
//單點查詢
int query(int l, int r, int pos, int x) {
if(l == r) return tree[x].sum;
pushDown(x, tree[x].k, tree[x].b);
int mid = (l + r) >> 1;
if(pos <= mid) return query(l, mid, pos, LNode);
else return query(mid+1, r, pos, RNode);
}
//區間修改
void update(int l, int r, int L, int R, int x, ll k, ll b) {
if(L <= l && R >= r) {
tree[x].k = tree[x].k * k % mod;
tree[x].b = (tree[x].b * k % mod+ b) % mod;
tree[x].sum = (tree[x].sum * k % mod + b) % mod;
return ;
}
pushDown(x, k, b);
int mid = (l + r) >> 1;
if(L <= mid) update(l, mid, L, R, LNode, k, b);
else update(mid+1, r, L, R, RNode, k, b);
pushUp(x);
}
int main() {
int T; scanf("%d", &T);
while(T --) {
int n, m, k; scanf("%d %d %d", &n, &m, &k);
build(1, n, 1); modify(1, n, 1, 1, 1);
for(int i = 1; i <= m; ++i) flag[i] = false;
for(int i = 1; i <= k; ++i) {
int op, a, b; scanf("%d %d %d", &op, &a, &b);
flag[op] = true; x[op] = a; y[op] = b;
}
for(int i = 1; i <= m; ++i) {
if(flag[i]) {//交換
int val1 = query(1, n, x[i], 1);
int val2 = query(1, n, y[i], 1);
modify(1, n, 1, x[i], val2);
modify(1, n, 1, y[i], val1);
} else {//計算
ll k = (n - 2) * inv(n) - 2 * inv(1ll * n * (n - 1));
k = (k % mod + mod) % mod;
ll b = 2 * inv(1ll * n * (n - 1));
b = (b % mod + mod) % mod;
update(1, n, 1, n, 1, k, b);
}
}
for(int i = 1; i <= n; ++i) printf("%d%c", query(1, n, i, 1), i == n? '\n' : ' ');
}
return 0;
}