• Codeforces 1260F Colored Tree


    Codeforces 1260F Colored Tree

    题意:

    给你一棵树,每个结点的颜色可能是([l_i,r_i])任一颜色,求所有可能的树的树价值总和。每棵树的价值表示所有颜色相同的两点间的距离总和。

    解法:

    考虑每种颜色对答案的贡献
    (v[i])表示(l_ileq Cleq r_i , g_i=(r_i-l_i+1) , P=prodlimits_{i=1}^n g_i)
    (Ans_C = sumlimits_{substack{v[i]wedge v[j]}} dis(i,j)*prodlimits_{substack{k eq iwedge k eq j}} g_k)
    (= sumlimits_{substack{v[i]wedge v[j]}} dis(i,j)* frac{P}{g_ig_j})
    (其中dis(i,j)=dep(i)+dep(j)-2dep(lca(i,j)))
    所以(Anc_C=P*sumlimits_{substack{v[i]wedge v[j]}} frac{dep(i)+dep(j)-2dep(lca(i,j))}{g_ig_j})
    (=P*(sumlimits_{substack{v[i]}} frac{dep(i)}{g_i}sumlimits_{substack{v[j]wedge j eq i}} frac{1}{g_j}-2sumlimits_{substack{v[i]wedge v[j]}} frac{2dep(lca(i,j))}{g_ig_j}))
    前半段很好维护。对于后半段,添加一个点x进入,就将1-x这条路径上的值加上(frac{1}{g_x}),而增加的答案是1-x这条路径原来的结点和乘上(frac{1}{g_x})

    比如我们插入一个点3,将3到1的路径每个点加上(frac{1}{g_3}),再插入一个点2,统计答案时就会加上1-2路径上的结点和,而这条路径上的结点和此时就是(dep(lca(2,3))*frac{1}{g_3})

    删除同理,做的时候考虑减去1这个点的贡献,因为节点数=dep+1。这样用树链剖分加上数据结构维护就可以。

    #include <bits/stdc++.h>
    #define lson rt << 1
    #define rson rt << 1 | 1
    #define ll long long
    using namespace std;
    const int maxn = 1e5;
    const ll mol = 1e9 + 7;
    
    int tot = 0;
    int n;
    int id[maxn + 11],top[maxn + 11],f[maxn + 11],son[maxn + 11],siz[maxn + 11],dep[maxn + 11];
    ll tree[4 * maxn + 11],lazy[4 * maxn + 11];
    ll g[maxn + 11];
    vector <int> edge[maxn + 11],in[maxn + 11],out[maxn + 11];
    
    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;
    		dep[v] = dep[x] + 1;
    		dfs(v , x);
    		siz[x] += siz[v];
    		if (siz[v] > siz[son[x]]) son[x] = v;
    	} 
    } 
    
    void dfs2(int x,int fa,int t) {
    	top[x] = t;
    	id[x] = ++tot;
    	if (son[x]) dfs2(son[x] , x , t);
    	for (auto v : edge[x]) {
    		if (v == fa || v == son[x]) continue;
    		dfs2(v , x , 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);
    } 
    
    void update(int x,ll val) {
    	while (x) {
    		update(1 , 1 , n , id[top[x]] , id[x] , val);
    		x = f[top[x]];
    	}
    } 
    
    ll query(int rt,int l,int r,int al,int ar) {
    	if (l > ar || r < al) return 0;
    	if (l >= al && r <= ar) return tree[rt];
    	if (lazy[rt]) push_down(rt , l , r);
    	int mid = (l + r) >> 1;
    	return add(query(lson , l , mid , al , ar) , query(rson , mid + 1 , r , al , ar));
    }
    
    ll query(int x) {
    	ll ans = 0;
    	while (x) {
    		ans = add(ans , query(1 , 1 , n , id[top[x]] , id[x]));
    		x = f[top[x]];
    	}
    	return ans;
    }
    
    int main(){
    	scanf("%d" , &n);
    	int m = 0;
    	ll p = 1;
    	for (int i = 1; i <= n; i++) {
    		int l,r;
    		scanf("%d %d",&l , &r);
    		g[i] = qpow(r - l + 1 , mol - 2);
    		p = p * (r - l + 1) % mol;
    		m = max(m , r);
    		in[l].emplace_back(i);
    		out[r + 1].emplace_back(i);
    	} 
    	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 , 0 , 1);
    	ll ans = 0;
    	ll last = 0;
    	ll sumg = 0;
    	ll sumd = 0;
    	for (int i = 1; i <= m; i++) {
    		for (auto x : out[i]) {
    			last = sub(last , g[x] * dep[x] % mol * sub(sumg , g[x]) % mol);
    			last = sub(last , sub(sumd , g[x] * dep[x] % mol) * g[x] % mol);
    			sumg = sub(sumg , g[x]);
    			sumd = sub(sumd , g[x] * dep[x] % mol);
    			update(x , sub(0 , g[x]));
    			last = add(last , 2 * sub(query(x) , query(1)) % mol * g[x] % mol);
    		}
    		for (auto x : in[i]) {
    			last = add(last , g[x] * dep[x] % mol * sumg % mol);
    			last = add(last , sumd * g[x] % mol);
    			sumg = add(sumg , g[x]);
    			sumd = add(sumd , g[x] * dep[x] % mol);
    			last = sub(last , 2 * sub(query(x) , query(1)) % mol * g[x] % mol);
    			update(x , g[x]);
    		} 
    		ans = add(ans , last);
    	} 
    	printf("%lld
    " , ans * p % mol);
    } 
    
    
  • 相关阅读:
    机器学习笔记之K近邻算法
    [C++基础]在子类中向父类的构造函数传递参数的小例子,包括类中常量的初始化
    POJ2709 染料贪心
    POJ2337 欧拉路径字典序输出
    POJ2337 欧拉路径字典序输出
    POJ1042 贪心钓鱼
    POJ3228二分最大流
    POJ3228二分最大流
    POJ3498最大流,枚举终点,企鹅,基础最大流
    POJ3498最大流,枚举终点,企鹅,基础最大流
  • 原文地址:https://www.cnblogs.com/Embiid/p/12273022.html
Copyright © 2020-2023  润新知