luogu P3345 [ZJOI2015]幻想乡战略游戏

算是一道动态点分治入门题(我太弱了加起来学了三天)。

拿到题首先考虑如何暴力,我们可以随机钦定一个点(不妨就设\(1\))为根节点,然后算出补给站设在该点的总花费,然后算出补给站在每个儿子时的总花费,显然最多只会有一个儿子花费比它(当前点)本身小(当且仅当\(sum[u]<sum[son] \times 2\)\(sum[i]\)表示\(i\)号点子树内拥有的军队总数),如果存在这个花费比它小的儿子,就跳到它儿子上重复上述过程,如果不存在这个儿子,那么这个点就是答案。

显然这样写一条链就没了,所以考虑用点分树来优化这个过程(下面其实就是动态点分治的套路了,跟本题关系不太大)。

记三个值\(sum[i],dis1[i],dis2[i]\)分别表示当前子树内的军队总数、当前子树内的点到当前子树根节点的总花费、当前子树内的点到当前子树根节点的父亲节点的总花费(注意这里所有子树的概念都是在点分树中)。

这三个值在修改是可以暴力跳点分树维护,查询的时候也是暴力跳点分树就好了。时间复杂度\(O(nlog^2n)\)

\(tips:\)今天学会了如何\(O(nlogn)\)预处理\(ST\)表然后\(O(1)\)\(LCA\),其实就是类似一个欧拉序的东西,按照\(dep\)建立\(ST\)表,然后就能\(O(1)\)得到\(LCA\)了。

代码:

#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define rint int

using namespace std;

typedef long long LL;
const int N=1000009;
int n,Q,root,head[N],cnt,F[N],log[N];
LL dis1[N],dis2[N],sum[N];
struct Edge
{
	int nxt,to,w;
}g[N*2];
struct G
{
	int head[N],cnt,f[N][30],dep[N],del[N],siz[N],Euler[N],Fst[N],Index;
	LL dis[N];
	struct Edge
	{
		int nxt,to,w;
	}g[N*2];
	
	void add(int from,int to,int w)
	{
		g[++cnt].nxt=head[from];
		g[cnt].to=to;
		g[cnt].w=w;
		head[from]=cnt;
	}
	int LCA(int x,int y)
	{
		if(Fst[x]>Fst[y])
			swap(x,y);
		x=Fst[x],y=Fst[y];
		int k=log[y-x+1];
		return dep[f[x][k]]<dep[f[y-(1<<k)+1][k]]?f[x][k]:f[y-(1<<k)+1][k];
	}
	void dfs(int x,int fa)
	{
		Euler[++Index]=x,Fst[x]=Index,f[Index][0]=x;
		for (rint i=head[x];i;i=g[i].nxt)
		{
			int v=g[i].to;
			if(v==fa)
				continue;
			dep[v]=dep[x]+1,dis[v]=dis[x]+g[i].w;
			dfs(v,x);
			Euler[++Index]=x,f[Index][0]=x;
		}
	}
	int Get_dis(int x,int y)
	{
		return dis[x]+dis[y]-2*dis[LCA(x,y)];
	}
	void DFS(int x,int fa)
	{
		siz[x]=1;
		for (rint i=head[x];i;i=g[i].nxt)
		{
			int v=g[i].to;
			if(v==fa||del[v])
				continue;
			DFS(v,x);
			siz[x]+=siz[v];
		}
	}
	int Get_Weight(int x)
	{
		DFS(x,-1);
		int k=siz[x]/2,fa=-1;
		while(1)
		{
			int tmp=0;
			for (rint i=head[x];i;i=g[i].nxt)
			{
				int v=g[i].to;
				if(v==fa||del[v])
					continue;
				if(siz[v]>siz[tmp])
					tmp=v;
			}
			if(siz[tmp]<=k)
				return x;
			fa=x,x=tmp;
		}
	}
	void work()
	{
		dfs(1,-1);
		for (int j=1;1<<j<=Index;j++)
			for (int i=1;i+(1<<j)-1<=Index;i++)
				if(dep[f[i][j-1]]<dep[f[i+(1<<j-1)][j-1]])
					f[i][j]=f[i][j-1];
				else
					f[i][j]=f[i+(1<<j-1)][j-1];
	}
}T;

void add(int from,int to,int w)
{
	g[++cnt].nxt=head[from];
	g[cnt].to=to;
	g[cnt].w=w;
	head[from]=cnt;
}

int read()
{
	char c=getchar();
	int x=0,f=1;
	while(c<'0'||c>'9')
	{
		if(c=='-')
			f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
		x=x*10+c-'0',c=getchar();
	return x*f;
}

void init()
{
	log[0]=-1;
	for (int i=1;i<=N-9;i++)
		log[i]=log[i>>1]+1;
	n=read(),Q=read();
	for (rint i=1;i<n;i++)
	{
		int x,y,z;
		x=read(),y=read(),z=read();
		T.add(x,y,z),T.add(y,x,z);
	}
	T.work();
}

void build(int fa)
{
	T.del[fa]=1;
	for (rint i=T.head[fa];i;i=T.g[i].nxt)
	{
		int v=T.g[i].to;
		if(T.del[v]||v==fa)
			continue;
		int w=T.Get_Weight(v);
		F[w]=fa,add(fa,w,v),add(w,fa,v);
		build(w);
	}
}

void Modify(int x,int y)
{
	//printf("eijfie %d %d\n",x,y);
	for (rint i=x;i;i=F[i])
	{
		sum[i]+=y;
		if(i!=root)
		{
			LL fuck=1LL*T.Get_dis(x,F[i])*y;
			//if(x==3&&y==1)
				//printf("???%d %d %d\n",x,F[i],T.Get_dis(2,3));
			dis1[F[i]]+=fuck,dis2[i]+=fuck;
		}
	}
}

LL calc(int x)
{
	LL ans=dis1[x];
	for (rint i=x;F[i];i=F[i])
	{
		ans+=dis1[F[i]]-dis2[i];
		ans+=(sum[F[i]]-sum[i])*T.Get_dis(x,F[i]);
	}
	return ans;
}

LL Query(int x)
{
	LL last=calc(x);
	//printf("fuck%d %d\n",x,last);
	for (rint i=head[x];i;i=g[i].nxt)
	{
		if(g[i].to==F[x])
			continue;
		LL now=calc(g[i].w);
		//printf("%lld %d\n",now,g[i].w);
		if(now<last)
			return Query(g[i].to);
	}
	return last;
}

void print(int x,int fa)
{
	printf("%d %d\n",fa,x);
	for (rint i=head[x];i;i=g[i].nxt)
	{
		int v=g[i].to;
		if(v==fa)
			continue;
		print(v,x);
	}
}

void work()
{
	root=T.Get_Weight(1);
	build(root);
	//printf("weoigwi%d\n",T.LCA(2,3));
	//print(root,-1);
	for (rint _=1;_<=Q;_++)
	{
		int x,y;
		x=read(),y=read();
		Modify(x,y);
		//if(_==4)
			//printf("wyj%d\n",dis1[2]);
		printf("%lld\n",Query(root));
	}
}

int main()
{
	init();
	work();
	return 0;
}
posted @ 2020-04-18 11:17  With_penguin  阅读(149)  评论(0编辑  收藏  举报