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\n" , 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\n" , add(ans , all)); } } }
来源:https://www.cnblogs.com/Embiid/p/12274213.html