AtCoder Beginner Contest 133 F.Colorful Tree(欧拉序+线段树)

题目

n(n<=1e5)个点的树,第i条边有u,v,c,d,u和v为边的两端,c为色号,d为边长

q(q<=1e5)个询问,第j次询问x,y,u,v,即把色号为x的所有边权都改成y之后,询问u到v的距离

思路来源

https://atcoder.jp/contests/abc133/submissions/6298312

题解

离线,把所有颜色为i的树边加到col[i]里,把所有颜色为j的询问加到color[j]里,

预处理点欧拉序,边欧拉序,用RMQ来解决LCA,sum[]维护当前点到根的链和

处理颜色i的询问时,ans=两条链之和-两条链上原颜色i树边之和+两条链上颜色i的边的个数*新改的值

做法,先把i的树边的条数插到bit1上,边权插到树状数组bit2上,

bit1中统计两条链上颜色i的边的个数,乘上新改的值v,从答案中减去

bit2中统计一下两条链上原颜色i树边之和,加到答案中

统计完之后,在BIT里把刚插入的颜色对应的边删掉,从而处理下一种颜色

 

本来在考虑,边是离散的,该如何解决,

现在了解到,边在欧拉序中也是对应一段区间的;像树剖一样,把边给远根节点,其欧拉序同远端节点

第i条边(连接u和v,且v是远根节点),入的时间戳同ID[v],出的时间戳同v出的时间戳

那么由于v出了之后,下一个出的就是u,这里不妨+1,变闭区间为开区间,i实际存在的为[in[i],out[i])

代码

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+10; 
const int lg=18;
int n,q;
int a,b,c,d,y,U,V,fa,ID;
int tmp,num,len1,len2;
int pos[N],ans[N],sum[N];//欧拉序中对应编号 答案 链上的和 
int bit1[N*2],bit2[N*2];//树按颜色建一棵 按查询建一棵 
int dep[N],dis[N];//点的深度 实际边边权
int head[N],v[N*2],nex[N*2],id[N*2],cnt;
int in[N],out[N];
int dfn[N*2],tot;
int dp[N*2][lg];
//欧拉序 dfn点戳 in out边戳 第i条边[in,out)
struct query{int id,y,u,v;};
vector<int>col[N];//把树边放入对应颜色 
vector<query>color[N];//把询问放入对应颜色 
int mn(int u,int v){if(dep[u]<dep[v])return u;else return v;};
void link(int x,int y,int i){v[++cnt]=y;nex[cnt]=head[x];id[cnt]=i;head[x]=cnt;}
void add(int *tr,int x,int v){for(int i=x;i<=tot;i+=i&-i)tr[i]+=v;}
int ask(int *tr,int x){int ans=0;for(int i=x;i>0;i-=i&-i)ans+=tr[i];return ans;}
void dfs(int u,int fa,int d)
{
	dfn[++tot]=u;
	pos[u]=tot;
	dep[u]=d;
	for(int i=head[u];i;i=nex[i])
	{
		int to=v[i],num=id[i],w=dis[num];//边的真实编号 
		if(to==fa)continue;
		in[num]=tot+1;//归远端节点 即下一个搜的节点 
		sum[to]=sum[u]+w;
		dfs(to,u,d+1);
		dfn[++tot]=u;
		out[num]=tot;
	}
}
void init(int up)
{
	for(int i=1;i<=up;++i)
	dp[i][0]=dfn[i];
	for(int len=1;(1<<len)<=up;++len)
	for(int i=1;i+(1<<len)-1<=up;++i)
	dp[i][len]=mn(dp[i][len-1],dp[i+(1<<(len-1))][len-1]);
} 
int lca(int u,int v)
{
	int l=pos[u],r=pos[v];
	if(l==r)return dp[l][0]; 
	if(l>r)swap(l,r);
	int k=log(r-l+1)/log(2);
	return mn(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<n;++i)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		link(a,b,i);link(b,a,i);
		col[c].pb(i);dis[i]=d;
	}
	dfs(1,-1,0); 
	init(tot);
	for(int i=1;i<=q;++i)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		color[a].pb(query{i,b,c,d});
	}
	for(int i=1;i<n;++i)
	{
		if(!color[i].size())continue;//没有询问 
		len1=col[i].size();
		for(int j=0;j<len1;++j)
		{
			num=col[i][j];
			add(bit1,in[num],1);
			add(bit1,out[num],-1);
			add(bit2,in[num],dis[num]);
			add(bit2,out[num],-dis[num]);
		}
		len2=color[i].size();
		for(int j=0;j<len2;++j)
		{
			ID=color[i][j].id;y=color[i][j].y;
			U=color[i][j].u;V=color[i][j].v;
			fa=lca(U,V);
			tmp=sum[U]+sum[V]-2*sum[fa];
			tmp-=(ask(bit2,pos[U])+ask(bit2,pos[V])-2*ask(bit2,pos[fa]));
			tmp+=y*(ask(bit1,pos[U])+ask(bit1,pos[V])-2*ask(bit1,pos[fa]));
			ans[ID]=tmp;
		}
		for(int j=0;j<len1;++j)
		{
			num=col[i][j];
			add(bit1,in[num],-1);
			add(bit1,out[num],1);
			add(bit2,in[num],-dis[num]);
			add(bit2,out[num],dis[num]);
		}
	}
	for(int i=1;i<=q;++i)
	printf("%d\n",ans[i]);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值