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
#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;};
vectorcol[N];//把树边放入对应颜色 
vectorcolor[N];//把询问放入对应颜色 
int mn(int u,int v){if(dep[u]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<r)swap(l,r);
	int k=log(r-l+1)/log(2);
	return mn(dp[l][k],dp[r-(1<

 

你可能感兴趣的:(#)