2020 CCPC-Wannafly Winter Camp Day2 --- F. 采蘑菇的克拉莉丝
题意:
解法:
考虑暴力解法,枚举起点的所有出边,拿边权乘以子树中蘑菇总数。最坏复杂度(O(nq))。
考虑轻重链剖分,每个点只考虑连向父亲的边的贡献,连向重儿子的边的贡献,所有轻儿子的贡献我们用一个tag记录下来。连向父亲的边贡献和连向重儿子的边都可以直接计算。
下面考虑所有轻儿子的贡献。修改点u的蘑菇数量,只可能对他的所有祖先结点的轻链答案有影响(对于一个点v不是u的祖先,那么u对v的贡献一定在指向父亲的边里计算了)。
那么我们从u向上跳重链,对轻链到达的所有点打上贡献标记即可,这样每次修改复杂度(O(logn))。
我们通过线段树维护子树内蘑菇大小就可以在(O(logn))的复杂度内求出连向父亲和重儿子的边的贡献。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6;
int n;
ll bit[maxn + 11];
int son[maxn + 11],siz[maxn + 11],id[maxn + 11],top[maxn + 11],fa[maxn + 11],weight[maxn + 11];
vector <pair<int,int> > edge[maxn + 11];
ll val[maxn + 11];
void dfs(int x,int f) {
fa[x] = f;
siz[x] = 1;
for (auto pi : edge[x]) {
int v = pi.first;
if (v == f) continue;
weight[v] = pi.second;
dfs(v , x);
siz[x] += siz[v];
if (siz[v] > siz[son[x]]) son[x] = v;
}
}
int tot = 0;
void dfs2(int x,int t) {
top[x] = t;
id[x] = ++tot;
if (!son[x]) return;
dfs2(son[x] , t);
for (auto pi : edge[x]) {
int v = pi.first;
if (v == fa[x] || v == son[x]) continue;
dfs2(v , v);
}
}
int lowbit(int x) { return x & (-x); }
void update(int x,int val) { for (; x <= n; x += lowbit(x)) bit[x] += val; }
ll sum(int x) {
ll ans = 0;
for ( ; x ; x -= lowbit(x)) ans += bit[x];
return ans;
}
ll query(int l,int r) { return sum(r) - sum(l - 1); }
ll calc(int v) {
ll ans = val[v];
if (son[v]) ans += query(id[son[v]] , id[son[v]] + siz[son[v]] - 1) * weight[son[v]];
if (v != 1) ans += (query(1 , n) - query(id[v] , id[v] + siz[v] - 1)) * weight[v];
return ans;
}
int main(){
scanf("%d" , &n);
for (int i = 1; i < n; i++) {
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
edge[u].push_back(make_pair(v , w));
edge[v].push_back(make_pair(u , w));
}
dfs(1 , 0);
dfs2(1 , 1);
int q;
scanf("%d" , &q);
int s = 1;
top[1] = 0;
while (q--) {
int op;
scanf("%d" , &op);
if (op == 1) {
int v,x;
scanf("%d %d",&v,&x);
update(id[v] , x);
while (v != 1) {
if (v == top[v]){
val[fa[v]] += 1ll * weight[v] * x;
v = fa[v];
}
else v = top[v];
}
}
else {
int v;
scanf("%d" , &v);
s = v;
}
printf("%lld
" , calc(s));
}
}
Codeforces 1254D Tree Queries
题意:
给定一棵树,每次进行两种操作,第一种操作给一个点v和一个值d,等概率从所有点中选一点r,对于所有u,若u->r的路径经过v,就将u的权值加上d。第二种操作求点v的期望权值。
解法:
对于每个操作一,我们可以推出,对于所有子树v外面的点,当r选在子树v内就可以通过v,所以期望增加(frac {d}{n}*siz(v));对于所有在子树v内的点,考虑在v的儿子y的子树中,那么
r选在v子树外或者v除了儿子y其他儿子的子树内都可以通过v,所以期望增加(frac {d}{n}*(n-siz(y)))
暴力做法,枚举v的所有儿子,对子树进行修改。
和上面一道题目类似,我们可以进行优化。同样只对重儿子进行修改,轻儿子统一计算。对于每个修改v,我们只会对他的子辈结点期望产生影响。(其他结点都在子树外,可以统一计算)这样我们对v的重儿子所对应的子树进行(O(logn))的线段树暴力修改,再将权值d记录在v结点上。统计答案时,每个结点的答案就是线段树记录的答案加上另外一部分,这部分就是这个结点向上跳遇到的所有轻链连接着的父亲上的tag所产生的答案。跳轻重链复杂度也是(O(logn))。
#include <bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
#define ll long long
using namespace std;
const ll mol = 998244353;
const int maxn = 150000;
ll tree[4 * maxn + 11],lazy[4 * maxn + 11];
ll tag[maxn + 11];
int siz[maxn + 11],f[maxn + 11],id[maxn + 11],top[maxn + 11],son[maxn + 11];
vector <int> edge[maxn + 11];
int tot = 0;
ll qpow(ll a,ll b) {
ll ans = 1;
while (b) {
if (b & 1) ans = ans * a % mol;
a = a * a % mol;
b >>= 1;
}
return ans;
}
void dfs(int x,int fa) {
siz[x] = 1;
f[x] = fa;
for (auto v : edge[x]) {
if (v == fa) continue;
dfs(v , x);
siz[x] += siz[v];
if (siz[v] > siz[son[x]]) son[x] = v;
}
}
void dfs2(int x,int t) {
top[x] = t;
id[x] = ++tot;
if (son[x]) dfs2(son[x] , t);
for (auto v : edge[x]) {
if (v == f[x] || v == son[x]) continue;
dfs2(v , v);
}
}
ll add(ll a,ll b) { a += b; if (a >= mol) a -= mol; return a; }
ll sub(ll a,ll b) { a -= b; if (a < 0) a += mol; return a; }
void push_up(int rt) { tree[rt] = add(tree[lson] , tree[rson]); }
void push_down(int rt,int l,int r) {
int mid = (l + r) >> 1;
ll val = lazy[rt]; lazy[rt] = 0;
tree[lson] = add(tree[lson] , val * (mid - l + 1) % mol); lazy[lson] = add(lazy[lson] , val);
tree[rson] = add(tree[rson] , val * (r - mid) % mol); lazy[rson] = add(lazy[rson] , val);
}
void update(int rt,int l,int r,int al,int ar,ll val) {
if (l > ar || r < al) return;
if (l >= al && r <= ar) {
tree[rt] = add(tree[rt] , val * (r - l + 1) % mol);
lazy[rt] = add(lazy[rt] , val);
return;
}
if (lazy[rt]) push_down(rt , l , r);
int mid = (l + r) >> 1;
update(lson , l , mid , al , ar , val);
update(rson , mid + 1 , r , al , ar , val);
push_up(rt);
}
ll query(int rt,int l,int r,int pos) {
if (l == r) return tree[rt];
if (lazy[rt]) push_down(rt , l , r);
int mid = (l + r) >> 1;
if (mid >= pos) return query(lson , l , mid , pos);
return query(rson , mid + 1 , r , pos);
}
int main() {
int n,q;
scanf("%d %d",&n,&q);
for (int i = 1; i < n; i++) {
int u,v;
scanf("%d %d",&u,&v);
edge[u].emplace_back(v);
edge[v].emplace_back(u);
}
dfs(1 , 0);
dfs2(1 , 1);
ll all = 0;
ll inv = qpow(n , mol - 2);
while (q--) {
int op,v;
scanf("%d %d",&op,&v);
if (op == 1) {
int d;
scanf("%d" , &d);
tag[v] = add(tag[v] , d);
all = add(all , inv * d % mol * siz[v] % mol);
if (son[v]) update(1 , 1 , n , id[son[v]] , id[son[v]] + siz[son[v]] - 1 , inv * d % mol * sub(n - siz[v] , siz[son[v]]) % mol);
}
else {
ll ans = add(query(1 , 1 , n , id[v]) , inv * tag[v] % mol * (n - siz[v]) % mol);
while (v) {
v = top[v];
ans = add(ans , tag[f[v]] * inv % mol * sub(n - siz[v] , siz[f[v]]) % mol);
v = f[v];
}
printf("%lld
" , add(ans , all));
}
}
}