zoukankan      html  css  js  c++  java
  • Luogu [ZJOI2015]幻想乡战略游戏

    显然,一棵树的带权重心最多只有两个,最少会有一个,而且在这两个点的答案一定相等。(都是带权重心当然相等)鉴于点分治的写法貌似并不太需要这样的分析,就不说了。我不会
    首先建出一颗点分树,然后考虑在点分树上跳儿子来保证复杂度,于是我们就需要快速算出所有点到这个重心的带权距离和。由于这题是点分树,考虑在点分树上维护一些信息来快速计算这个东西。
    对于我们当前枚举到的这个点(x),一部分答案贡献来自点分树上(x)的子树以内,这部分可以直接记录一个(dsum[x])表示在点分树上(x)的子树内的所有点到(x)的距离和。另一部分来自子树外,我们先只考虑(x)在点分树上的父节点(fa[x])的子树里的点对这里的贡献,至于(fa[fa[x]])可以以此类推。那么这个贡献就又分为两部分,一部分是(fa[x])的子树内根不为(x)的子树内的点到(fa[x])的距离。这个时候显然如果直接使用(dsum[fa[x]])会导致重复计算,所以要记录一个(fsum[x])来表示(x)这个子树内的所有点到(fa[x])的距离和。那么这一部分的答案贡献就是(dsum[fa[x]]-fsum[x])。那么还有一部分的答案就是这些点都到了(fa[x])之后再到(x)的距离和,那这部分的答案就是(dis(fa[x],x) imes(ssum[fa[x]]-ssum[x]))。这里的(ssum[x])就是(x)子树内的(size)之和。(dis)的处理也需要树剖来维护。然后向上爬点分树的时候注意一下,哪些地方应该用原始的(x),哪些地方应该用向上跳了的结点就可以了。具体的实现在代码里。时间复杂度是(O(nlogn^3)),由于树剖常数小,可以跑过。
    然后我写的时候有一个想法,在于为什么不能直接维护一个点和根的关系呢?这样不就(O(1))了吗?后来想想发现,我们的算法是基于外部的点要到(x)必须要经过(x)在点分树上的祖先才能够做到枚举的。这点要想通我个人认为还是很重要的。
    upd:一个要注意的一点是在求答案的时候要在原树上找儿子,在点分树上跳重心。

    #include <cstdio>
    using namespace std;
    #define R register
    #define LL long long
    const int MAXN=5e5+10;
    inline int read() {
    	int x=0,f=1; char a=getchar();
    	for(;a>'9'||a<'0';a=getchar()) if(a=='-') f=-1;
    	for(;a>='0'&&a<='9';a=getchar()) x=x*10+a-'0';
    	return x*f;
    }
    inline int max(int x,int y) { return x > y ? x : y; }
    inline int min(int x,int y) { return x < y ? x : y; }
    int n,q;
    namespace Graph {
    	int cnt,head[MAXN];
    	struct edge { int to,w,next; } e[MAXN<<1];
    	inline void add(int x,int y,int w) {
    		e[++cnt]={y,w,head[x]}; head[x]=cnt; 
    	}
    	int dep[MAXN],dis[MAXN],fa[MAXN],son[MAXN],siz[MAXN],top[MAXN];
    	inline void dfs1(int x,int fx) {
    		fa[x]=fx; dep[x]=dep[fx]+1; siz[x]=1;
    		for(R int i=head[x];i;i=e[i].next) {
    			int y=e[i].to; if(y==fx) continue;
    			dis[y]=dis[x]+e[i].w; dfs1(y,x); siz[x]+=siz[y];
    			if(siz[y]>siz[son[x]]) son[x]=y;
    		}
    	}
    	inline void dfs2(int x,int topx) {
    		top[x]=topx;
    		if(son[x]) dfs2(son[x],topx);
    		for(R int i=head[x];i;i=e[i].next) {
    			int y=e[i].to; 
    			if(y==fa[x]||y==son[x]) continue;
    			dfs2(y,y);
    		}
    	}
    	inline int Lca(int x,int y) {
    		while(top[x]!=top[y]) 
    			if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
    			else y=fa[top[y]];
    		return dep[x]<dep[y]?x:y;
    	}
    	inline int Dist(int x,int y) {
    		return dis[x]+dis[y]-dis[Lca(x,y)]*2;
    	}
    	inline void Build() {
    		dfs1(1,0); dfs2(1,1);
    	}
    }
    namespace Tree {
    	int Cnt,Head[MAXN];
    	struct Edge { int to,rt,next; } E[MAXN<<1];
    	inline void Add(int x,int y,int rt) {
    		E[++Cnt]={ y, rt, Head[x] }; Head[x]=Cnt; 
    	}
    	int RT,Root,mx[MAXN],vis[MAXN],siz[MAXN],total,fa[MAXN];
    	inline void findrt(int x,int fx) {
    		mx[x]=0; siz[x]=1;
    		for(R int i=Graph::head[x];i;i=Graph::e[i].next) {
    			int y=Graph::e[i].to;
    			if(y==fx||vis[y]) continue;
    			findrt(y,x); siz[x]+=siz[y];
    			mx[x]=max(mx[x],siz[y]);
    		}
    		mx[x]=max(mx[x],total-siz[x]);
    		if(mx[x]<mx[Root]) Root=x;
    	}
    	inline void build(int x,int fx) {
    		fa[x]=fx; vis[x]=1;
    		for(R int i=Graph::head[x];i;i=Graph::e[i].next) {
    			int y=Graph::e[i].to; if(vis[y]) continue;
    			total=siz[y]; Root=0;
    			findrt(y,0); Add(x,y,Root); 
    			build(Root,x);
    		}
    	}
    	inline void Build() {
    		mx[0]=0x7f7f7f7f;
    		Root=0; findrt(1,0);findrt(Root,0); RT=Root; build(Root,0);
    	}
    	LL sums[MAXN],sumd[MAXN],sumf[MAXN];
    	inline void Modify(int x,int k) {
    		sums[x]+=k;
    		for(R int i=x;fa[i];i=fa[i]) {
    			int len=Graph::Dist(x,fa[i]);
    			sums[fa[i]]+=k;
    			sumd[fa[i]]+=1LL*k*len;
    			sumf[i]+=1LL*k*len;
    		}
    	}
    	inline LL Count(int x) {
    		LL res=sumd[x];
    		for(R int i=x;fa[i];i=fa[i]) {
    			int len=Graph::Dist(x,fa[i]);
    			res+=(sums[fa[i]]-sums[i])*len;
    			res+=sumd[fa[i]]-sumf[i];
    		}
    		return res;
    	}
    	inline LL Ask(int x) {
    		LL res=Count(x);
    		for(R int i=Head[x];i;i=E[i].next) {
    			int y=E[i].to;
    			if(Count(y)<res) return Ask(E[i].rt);
    		}
    		return res;
    	}
    	inline LL Ask() { return Ask(RT); }
    }
    int main() {
    //	freopen("a.in","r",stdin);
    //	freopen("a.out","w",stdout);
    	n=read(); q=read();
    	for(R int i=1;i<n;i++) {
    		int x=read(),y=read(),z=read();
    		Graph::add(x,y,z); Graph::add(y,x,z);
    	}
    	Graph::Build(); Tree::Build();
    	while(q--) {
    		int x=read(),y=read();
    		Tree::Modify(x,y);
    		printf("%lld
    ",Tree::Ask());
    	}
    	return 0;
    }
    
  • 相关阅读:
    王者齐聚!Unite 2017 Shanghai 日程讲师全揭晓
    微软在.NET官网上线.NET 架构指南频道
    期待微软平台即服务技术Service Fabric 开源
    Visual Studio 20周年软件趋势随想
    .NET 十五岁,谈谈我眼中的.NET
    API网关Ocelot 使用Polly 处理部分失败问题
    互联网背景下知识半衰期这么短,如何学习?
    CentOS 7 上面安装PowerShell
    搭建consul 集群
    Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制
  • 原文地址:https://www.cnblogs.com/clover4/p/12832861.html
Copyright © 2011-2022 走看看