[COGS2278]树黑白

这是一题动态点分治qwq
动态点分治就是把原树中的重心拿出来构成了一棵树,然后每个重心记录它的联通块的信息,这样以后修改u就只会影响u到root路径上所有的重心点的信息,查询也可以只用访问u到root路径上所有的重心点。
具体看代码注释吧。。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int num,head[400005],vet[400005],nex[400005],val[400005];
int numl,headl[8000005],root[8000005],son[8000005],nexl[8000005],dis[8000005];
int n,m,s,rt,id,size[200005],f[200005],sum[400005],Num[400005],col[200005];
//num[rt]表示rt的所有子树中黑点数量,sum[rt]表示rt的所有子树中所有黑点到rt的距离之和
bool vis[200005];
void add(int u,int v,int l)
{
	num++;
	vet[num]=v;
	val[num]=l;
	nex[num]=head[u];
	head[u]=num;
}
void addl(int u,int rt,int sontree,int d) //表示u的祖先是rt,u在rt的sontree子树中,和rt距离为d
{
	numl++;
	root[numl]=rt;
	son[numl]=sontree;
	dis[numl]=d;
	nexl[numl]=headl[u];
	headl[u]=numl;
}
void getroot(int u,int fa)//求重心rt
{
	size[u]=1; f[u]=1;
	for (int i=head[u]; i; i=nex[i])
	{
		int v=vet[i];
		if (!vis[v] && v!=fa)
		{
			getroot(v,u);
			size[u]+=size[v];
			f[u]=max(f[u],size[v]);
		}
	}
	f[u]=max(f[u],s-size[u]);
	if (f[u]<f[rt]) rt=u;
}
void build(int u,int fa,int rt,int sontree,int d)//建立新重心树
{
	addl(u,rt,sontree,d);
	for (int i=head[u]; i; i=nex[i])
	{
		int v=vet[i];
		if (!vis[v] && v!=fa) build(v,u,rt,sontree,d+val[i]);
	} 
}
void dfs(int u)
{
	vis[u]=1;
	id++;
	int idu=id;
	addl(u,idu,0,0);
	for (int i=head[u]; i; i=nex[i])
	{
		int v=vet[i];
		if (!vis[v])
		{
			id++;
			build(v,u,idu,id,val[i]);
		}
	}
	for (int i=head[u]; i; i=nex[i])
	{
		int v=vet[i];
		if (!vis[v])
		{
			s=size[v];
			rt=0;
			getroot(v,0);
			dfs(rt);
		}
	}
}
void change(int u)//修改
{
	int ff=-1;
	if (!col[u]) ff=1;
	for (int i=headl[u]; i; i=nexl[i])
	{
		int rt=root[i],sontree=son[i];
		Num[rt]+=ff; sum[rt]+=ff*dis[i];
		if (sontree) Num[sontree]+=ff,sum[sontree]+=ff*dis[i];
	}
	col[u]^=1;
}
int query(int u)
{
	int ans=0;
	for (int i=headl[u]; i; i=nexl[i])
	{
		int rt=root[i],sontree=son[i];
		ans+=dis[i]*(Num[rt]-Num[sontree])+sum[rt]-sum[sontree];
		//u在rt的子树中对答案贡献=u到rt的距离*rt的子树中除了sontree外(u所在子树)所有的黑点的数量+rt的子树除了sontree外所有黑点到rt的距离
	}
	return ans;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i=1; i<n; i++)
	{
		int u,v,l;
		scanf("%d%d%d",&u,&v,&l);
		add(u,v,l);
		add(v,u,l);
	}
	s=n;
	rt=0;
	f[0]=1e9;
	getroot(1,0);
	dfs(rt);
	for (int i=1; i<=m; i++)
	{
		char ch[10];
		int x;
		scanf("%s%d",ch,&x);
		if (ch[0]=='M') change(x);
		else printf("%d\n",query(x));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值