• [ZJOI2015] 地震后的幻想乡


    前言

    点分树练习题。

    题目

    洛谷

    LOJ

    讲解

    我们发现这道题相当于找树上带权重心。

    有一个我认为挺nb的贪心是从根开始走,每次走最优的那个儿子,走不动了就是答案,而且对于每个当前点,至多只会有一个儿子比它优。

    只考虑走的话,复杂度是 \(O(depth)\) 的,考虑优化它,理所当然会想到点分树,再结合每个点度数不超过 20 这个条件,做法基本就明晰了。

    从点分树的根开始,在原树上找最优的儿子,走点分树上的边。

    现在问题是怎么算花费,其实我们每次修改的时候在点分树上改,查询也可以在点分树上查,记录到点分树上的点的父亲的花费,把这个花费在父亲和该点都记录一下,就可以实现 \(O(\log_2n)\) 查询花费了。当然也需要记录点分树子树的权值和。

    为了保证复杂度需要 \(O(1)\) LCA,总复杂度是 \(O(20n\log_2^2n)\)

    代码

    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 200005;
    int n,Q;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int hd[MAXN],head[MAXN],tot;
    struct edge{
    	int v,w,nxt;
    }e[MAXN<<2];
    void Add_Edge(int u,int v,int w){
    	e[++tot] = edge{v,w,head[u]};
    	head[u] = tot;
    }
    void Add_Double_Edge(int u,int v,int w){
    	Add_Edge(u,v,w);
    	Add_Edge(v,u,w);
    }
    
    int dfn[MAXN],dfntot,st[MAXN][19],d[MAXN],who[MAXN],LG[MAXN];
    int up(int u,int v){return d[u] < d[v] ? u : v;}
    int lca(int u,int v){
    	u = who[u]; v = who[v];
    	if(u > v) swap(u,v);
    	int k = LG[v-u+1];
    	return up(st[u][k],st[v-(1<<k)+1][k]);
    }
    int dis(int u,int v){if(!u) return 0;return d[u] + d[v] - (d[lca(u,v)] << 1);}
    void dfs1(int x,int fa){
    	st[who[x] = ++dfntot][0] = x;
    	for(int i = head[x],v; i ;i = e[i].nxt){
    		if((v = e[i].v) == fa) continue;
    		d[v] = d[x] + e[i].w; dfs1(v,x);
    		st[++dfntot][0] = x;
    	}
    }
    int rt,siz[MAXN],MAX[MAXN],RT;
    bool vis[MAXN];
    void getrt(int x,int fa,int S){
    	siz[x] = 1; MAX[x] = 0;
    	for(int i = head[x],v; i ;i = e[i].nxt){
    		if(vis[v = e[i].v] || v == fa) continue;
    		getrt(v,x,S); siz[x] += siz[v];
    		MAX[x] = Max(MAX[x],siz[v]);
    	}
    	MAX[x] = Max(MAX[x],S-siz[x]);
    	if(!rt || MAX[x] < MAX[rt]) rt = x;
    }
    void dfs3(int x,int fa){
    	siz[x] = 1; 
    	for(int i = head[x],v; i ;i = e[i].nxt){
    		if(vis[v = e[i].v] || v == fa) continue;
    		dfs3(v,x); siz[x] += siz[v];
    	}
    }
    int f[MAXN];
    void dfs2(int x){
    	vis[x] = 1; dfs3(x,0);
    	for(int i = head[x],v; i ;i = e[i].nxt){
    		e[i].w = 0;
    		if(vis[v = e[i].v]) continue; 
    		rt = 0; getrt(v,x,siz[v]); f[rt] = x; e[i].w = rt;
    		dfs2(rt);
    	}
    }
    LL sd[MAXN],dis1[MAXN],dis2[MAXN];
    LL calc(int x){
    	LL ret = dis1[x];
    	for(int i = x,dd; i ;i = f[i]){
    		dd = dis(f[i],x);
    		ret += dis1[f[i]] - dis2[i];
    		ret += 1ll * dd * (sd[f[i]] - sd[i]);
    	}
    	return ret;
    }
    LL Query(int x){
    	LL ret = calc(x);
    	for(int i = head[x]; i ;i = e[i].nxt){
    		if(!e[i].w) continue;
    		LL tmp = calc(e[i].v);
    		if(tmp < ret) return Query(e[i].w);
    	}
    	return ret;
    }
    
    int main()
    {
    //	freopen("tree.in","r",stdin);
    //	freopen("tree.out","w",stdout);
    	n = Read(); Q = Read();
    	for(int i = 1,u,v;i < n;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
    	dfs1(1,0);
    	LG[0] = -1;
    	for(int i = 1;i <= dfntot;++ i) LG[i] = LG[i>>1] + 1;
    	for(int i = dfntot;i >= 1;-- i)
    		for(int j = 1;i+(1<<j)-1 <= dfntot;++ j)
    			st[i][j] = up(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    	getrt(1,0,n);
    	dfs2(RT = rt);
    	while(Q --> 0){
    		int x = Read(),val = Read(); sd[x] += val;
    		for(int i = x,dd; i ;i = f[i]){
    			dd = dis(f[i],x);
    			dis1[f[i]] += 1ll * dd * val;
    			dis2[i] += 1ll * dd * val;
    			sd[f[i]] += val;
    		}
    		Put(Query(RT),'\n');
    	}
    	return 0;
    }
    /*
    点分
    思考怎么修改,爬点分树,乱改 
    似乎我只需要在点分树里面改贡献就好了 
    */
    
  • 相关阅读:
    Makefile的常用技术总结
    NPOI 插入行[转]
    LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别 [转]
    Using Google Public DNS[Google公共DNS服务器]
    软件开发知识[TDD]
    MySQL函数之STRCMP()
    MySQL知识[INSERT语法]
    软件开发知识[ORM]
    软件开发知识[ADO.NET Entity Framework]
    mysql workbench 在模板与数据库间同步
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/16036908.html
Copyright © 2020-2023  润新知