Embiid  

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));
		}
	}
} 

posted on 2020-02-07 19:28  Embiid  阅读(138)  评论(0编辑  收藏  举报