树链剖分个人题目总结

首先,想要学树链剖分基础的朋友们很抱歉请换篇博客看,,,这是个题目总结
//
本蒟蒻求不被大佬嘲讽。。。。
//
树剖啊啊,记得高一时因“运输计划”一题需要此算法,还不会线段树的我便去网上看。。。嗯嗯。。结果可想而知。。从此有了心理阴影。。
其实树剖没那么恶心,学完以后才发现,其实树剖是很简单的。。。

这里不再冗余地讲怎样做树剖,而是总结本蒟蒻所见的树剖题目。
 bzoj2836
题意:一棵树支持两种操作:1.将它的子树上所有节点值+v;2.询问u到v上所有点的权值和;
要点:
1.怎么维护子树的修改?
要通过dfs序维护。我们知道在树剖的dfs2中会通过dfs序来维护一个节点在线段树中所对应的位置,很明显,u的dfs序与遍历完子树回到u时最后一个dfs序刚好囊括了u和它的子树们在线段树中的位置,所以记录一个点的dfs前序与后序即可实现直接修改子树。
2.询问u到v
先询问u到lca(u,v)再询问v到lca(u,v),最后把lca(u,v)的值减去即可
这明显是道一眼树剖题。。。。
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#pragma comment(linker, "/STACK:10240000000,10240000000")//手动开大栈区 
#define lson rt*2
#define rson rt*2+1
#define mid (l+r)/2
using namespace std;
const int N=100050;
typedef long long ll;
int head[N],to[N*2],next[N*2],fa[N][35],cnt;
void add(int f,int t)
{
	next[++cnt]=head[f];
	to[cnt]=t;
	head[f]=cnt;
}
//
int n,q,a,b,c;
ll d;
//
int son[N],size[N],dep[N];
void dfs1(int rt)
{ 
	size[rt]=1;
	int flag=0;
	for(int i=head[rt];i!=-1;i=next[i])
	{
		dep[to[i]]=dep[rt]+1;
		dfs1(to[i]);
		size[rt]+=size[to[i]];
		if(size[to[i]]>flag)
			flag=size[to[i]],son[rt]=to[i];
	}
}
int zhe[N],fan[N],mx[N],top[N],num;
void dfs2(int rt,int root)
{
	top[rt]=root;
	zhe[rt]=++num;
	fan[num]=rt;
	if(son[rt])
		dfs2(son[rt],root);
	for(int i=head[rt];i!=-1;i=next[i])
		if(to[i]!=son[rt])
			dfs2(to[i],to[i]);
	mx[rt]=num;
}
//
void init()
{
	for(int j=1;j<=30;j++)
		for(int i=1;i<=n;i++)
			if(fa[i][j-1]!=0)
				fa[i][j]=fa[fa[i][j-1]][j-1];
}
int er[31]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824};
int lca(int u,int v)
{
	if(dep[u]<dep[v])
		swap(u,v);
	int x=dep[u]-dep[v];
	for(int i=30;i>=0;i--)
	{
		if(x>=er[i])
			u=fa[u][i],x-=er[i];
	}
	if(u==v)
		return u;
	for(int i=31;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
struct node
{
	ll sum,flag;
}tree[4*N];
void pushup(int rt)
{
	tree[rt].sum=tree[lson].sum+tree[rson].sum;
}
void pushdown(int rt,int l,int r)
{
	if(!tree[rt].flag)
		return ;
	tree[lson].sum+=(mid-l+1)*tree[rt].flag;
	tree[rson].sum+=(r-mid)*tree[rt].flag;
	tree[lson].flag+=tree[rt].flag;
	tree[rson].flag+=tree[rt].flag;
	tree[rt].flag=0;
}
void ins(int rt,int l,int r,int L,int R,ll x)
{
	if(l==L&&r==R)
	{
		tree[rt].sum+=(r-l+1)*x;
		tree[rt].flag+=x;
		return ;
	}
	pushdown(rt,l,r);
	if(mid>=R)
		ins(lson,l,mid,L,R,x);
	else if(mid<L)
		ins(rson,mid+1,r,L,R,x);
	else 
		ins(lson,l,mid,L,mid,x),ins(rson,mid+1,r,mid+1,R,x);
	pushup(rt);
}
void chang(int u,int v,ll x)
{
	int aim=lca(u,v);
	if(u==v)
	{
		ins(1,1,n,zhe[u],zhe[u],x);
		return ;
	}
	int cnt1=0;
	while(u!=aim)
	{
		if(dep[top[u]]>dep[aim])
			ins(1,1,n,zhe[top[u]],zhe[u],x),u=fa[top[u]][0];
		else
			ins(1,1,n,zhe[aim],zhe[u],x),u=aim,cnt1++;
	}
	while(v!=aim)
	{
		if(dep[top[v]]>dep[aim])
			ins(1,1,n,zhe[top[v]],zhe[v],x),v=fa[top[v]][0];
		else
			ins(1,1,n,zhe[aim],zhe[v],x),v=aim,cnt1++;
	}
	if(cnt1==0)
		ins(1,1,n,zhe[aim],zhe[aim],x);
	else if(cnt==2)
		ins(1,1,n,zhe[aim],zhe[aim],-x);
}
ll query(int rt,int l,int r,int L,int R)
{
	if(l==L&&r==R)
		return tree[rt].sum;
	pushdown(rt,l,r);
	if(mid>=R)
		return query(lson,l,mid,L,R);
	else if(mid<L)
		return query(rson,mid+1,r,L,R);
	else 
		return query(lson,l,mid,L,mid)+query(rson,mid+1,r,mid+1,R);
}
char f[55];
int main()
{
//	freopen("tree.in","r",stdin);
//	freopen("tree.ans","w",stdout);
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1;i<n;i++)
		scanf("%d%d",&a,&b),add(a+1,b+1),fa[b+1][0]=a+1;
	init();
	dfs1(1);
	dfs2(1,1);
	scanf("%d",&q);
	while(q--)
	{
		scanf("%s",f);
		if(f[0]=='A')
		{
			scanf("%d%d%lld",&b,&c,&d);
			chang(b+1,c+1,d);
		}
		else
			scanf("%d",&b),printf("%lld\n",query(1,1,n,zhe[b+1],mx[b+1]));
	}
	fclose(stdin);
	fclose(stdout);
	return 0; 
}

bzoj1984
题意:一棵树,点上无权而边上有值,现有四种操作:
将一个边的值修改,将一个点到另一个点上所有边修改,将一个点到另一个上所有边加上一个值,询问一个点到另一个点上所有边中的最大值。
要点:
1.是边权而非点权
因为这是棵树,所以在确定了根以后我们可以将一个边的值赋到指向的儿子节点上,便可以做树剖了。
2.怎么又改值又加值
若一个值被改,那么之前所加的值都会无效,所以,线段树维护两个延迟标记,标记1表示它应该先被改成什么值,标记2表示完成1后它应该加上多少值,pushdown时首先看标记1是否为-1(即存不存在改值操作),若存在,那么先将左右子节点值直接改,并将它们的标记1更改为当前节点的标记1,然后清零它们的标记2。之后给两个子节点加上当前子节点的标记2值,然后给两个标记2也加上自己的值。
3.细节
注意在边权化点权的情况下,要注意在修改时将lca(u,v)避过去。
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdio.h>
#define lson rt*2
#define rson rt*2+1
#define mid (l+r)/2
using namespace std;
const int N=100050;
int n;
int head[N],to[N*2],next[2*N],v2[2*N],v1[2*N],id[2*N],fan[N],cnt;
void add(int f,int t,int val,int hao)
{
	to[++cnt]=t;
	id[cnt]=hao;
	v1[cnt]=val;
	next[cnt]=head[f];
	head[f]=cnt;
}
int a,b,c;
int size[N],son[N];
int fa[N][25];
int s[25],dep[N];
void dfs1(int x)
{
	dep[x]=dep[fa[x][0]]+1;
	size[x]=1;
	for(int i=1;i<=16;i++)
	{
		if(s[i]>=dep[x])
			break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(to[i]==fa[x][0])
			continue;
		fa[to[i]][0]=x;
		fan[id[i]]=to[i];
		v2[to[i]]=v1[i];
		dfs1(to[i]);
		size[x]+=size[to[i]];
		if(size[to[i]]>size[son[x]])
			son[x]=to[i];
	}
}
int zhe[N],top[N],num;
void dfs2(int x,int root)
{
	zhe[x]=++num;
	top[x]=root;
	if(son[x])
		dfs2(son[x],root);
	for(int i=head[x];i!=-1;i=next[i])
		if(to[i]!=son[x]&&to[i]!=fa[x][0])
			dfs2(to[i],to[i]);
}
int lca(int u,int v)
{
	if(dep[u]<dep[v])
		swap(u,v);
	int cha=dep[u]-dep[v];
	for(int i=16;i>=0;i--)
		if(cha&s[i])
			u=fa[u][i];
	if(u==v)
		return v;
	for(int i=16;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
struct node
{
	int mx,flag,flah;
}tree[N*8];
void pushup(int rt)
{
	tree[rt].mx=max(tree[lson].mx,tree[rson].mx);
}
void pushdown(int rt,int l,int r)
{
	if(!tree[rt].flag&&tree[rt].flah==-1)
		return;
	if(l==r)
		return ;
	if(tree[rt].flah!=-1)
	{
		tree[lson].flag=tree[rson].flag=0;
		tree[lson].flah=tree[rson].flah=tree[rt].flah;
		tree[lson].mx=tree[rson].mx=tree[rt].flah;
		tree[rt].flah=-1;
	}
	tree[lson].flag+=tree[rt].flag;
	tree[rson].flag+=tree[rt].flag;
	tree[lson].mx+=tree[rt].flag;
	tree[rson].mx+=tree[rt].flag;
	tree[rt].flag=0;
}
void ins1(int rt,int l,int r,int pos,int val)
{
	pushdown(rt,l,r);
	if(l==r)
	{
		tree[rt].mx=val;
		return ;
	}
	if(mid<pos)
		ins1(rson,mid+1,r,pos,val);
	if(mid>=pos)
		ins1(lson,l,mid,pos,val);
	pushup(rt);
}
void ins2(int rt,int l,int r,int L,int R,int val)
{
	pushdown(rt,l,r);
	if(l==L&&r==R)
	{
		tree[rt].flag+=val;
		tree[rt].mx+=val;
		return ;
	}
	if(mid>=R)
		ins2(lson,l,mid,L,R,val);
	else	if(mid<L)
		ins2(rson,mid+1,r,L,R,val);
	else
		ins2(lson,l,mid,L,mid,val),ins2(rson,mid+1,r,mid+1,R,val);
	pushup(rt);
}
void ins3(int rt,int l,int r,int L,int R,int val)
{
	pushdown(rt,l,r);
	if(l==L&&r==R)
	{
		tree[rt].flah=val;
		tree[rt].flag=0;
		tree[rt].mx=val;
		return ;
	}
	if(mid>=R)
		ins3(lson,l,mid,L,R,val);
	else	if(mid<L)
		ins3(rson,mid+1,r,L,R,val);
	else
		ins3(lson,l,mid,L,mid,val),ins3(rson,mid+1,r,mid+1,R,val);
	pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
	pushdown(rt,l,r);
	if(l==L&&r==R)
		return tree[rt].mx;
	if(mid>=R)
		return query(lson,l,mid,L,R);
	else	if(mid<L)
		return query(rson,mid+1,r,L,R);
	else
		return max(query(lson,l,mid,L,mid),query(rson,mid+1,r,mid+1,R));
}
void in1(int u,int v,int val)
{
	while(top[u]!=top[v])
	{
		ins2(1,1,n,zhe[top[u]],zhe[u],val);
		u=fa[top[u]][0];
	}
	if(zhe[v]+1<=zhe[u])
	ins2(1,1,n,zhe[v]+1,zhe[u],val);
}
void in2(int u,int v,int val)
{
	while(top[u]!=top[v])
	{
		ins3(1,1,n,zhe[top[u]],zhe[u],val);
		u=fa[top[u]][0];
	}
	if(zhe[v]+1<=zhe[u])
	ins3(1,1,n,zhe[v]+1,zhe[u],val);
}
int qu(int u,int v)
{
	int res=0;
	while(top[u]!=top[v])
	{
		res=max(query(1,1,n,zhe[top[u]],zhe[u]),res);
		u=fa[top[u]][0];
	}
	if(zhe[v]+1<=zhe[u])
	res=max(query(1,1,n,zhe[v]+1,zhe[u]),res);
	return res;
}
char cc[10];
int x,y;
int main()
{
	memset(head,-1,sizeof(head));
	s[0]=1;
	for(int i=1;i<=16;i++)
		s[i]=s[i-1]*2;
	scanf("%d",&n);
	for(int i=1;i<n;i++)
		scanf("%d%d%d",&a,&b,&c),add(a,b,c,i),add(b,a,c,i);
	dfs1(1);
	dfs2(1,1);
	for(int i=2;i<=n;i++)
		ins1(1,1,n,zhe[i],v2[i]);
	while(1)
	{
		scanf("%s",cc);
		if(cc[0]=='S')
			break;
		else if(cc[0]=='A')
		{
			scanf("%d%d%d",&a,&b,&c);
			int f=lca(a,b);
			if(f!=a)	in1(a,f,c);
			if(f!=b)	in1(b,f,c);
		}
		else if(cc[0]=='M')
		{
			scanf("%d%d",&a,&b);	
			int f=lca(a,b);
			int res=0;
			if(f!=a)	res=max(qu(a,f),res);
			if(f!=b)	res=max(qu(b,f),res);
			printf("%d\n",res);
		}
		else if(cc[0]=='C'&&cc[1]=='o')
		{
			scanf("%d%d%d",&a,&b,&c);
			int f=lca(a,b);
			if(f!=a)	in2(a,f,c);
			if(f!=b)	in2(b,f,c);
		}
		else if(cc[0]=='C'&&cc[1]=='h')
		{
			scanf("%d%d",&a,&b);
			ins1(1,1,n,zhe[fan[a]],b);
		}
	}
}

bzoj4034
题意:一棵树,修改子树,修改单点,询问点到根和。
没什么要点。。。实在太裸了。。。
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#define lson rt*2
#define rson rt*2+1
#define mid (l+r)/2
using namespace std;
const int N=100050;
typedef long long ll;
int m,n;
int head[N],next[2*N],to[2*N],cnt;
void add(int f,int t)
{
	to[++cnt]=t;
	next[cnt]=head[f];
	head[f]=cnt;
}
ll v[N];
int a,b;
ll c;
struct node
{
	ll sum,flag;
}tree[N*4];
ll size[N];
int son[N],fa[N];
void dfs1(int rt,int last)
{
	ll flag=0;
	size[rt]=1;
	for(int i=head[rt];i!=-1;i=next[i])
	{
		if(to[i]==last)
			continue;
		fa[to[i]]=rt;
		dfs1(to[i],rt);
		size[rt]+=size[to[i]];
		if(size[to[i]]>flag)
			flag=size[to[i]],son[rt]=to[i];
	}
}
int num,fan[N],zhe[N],top[N],mx[N];
ll vl[N];
void dfs2(int rt,int root)
{
	zhe[rt]=++num;
	fan[num]=rt;
	vl[num]=v[rt];
	top[rt]=root;
	if(son[rt])
		dfs2(son[rt],root);
	for(int i=head[rt];i!=-1;i=next[i])
		if(to[i]!=fa[rt]&&to[i]!=son[rt])
			dfs2(to[i],to[i]);
	mx[rt]=num;
}
void pushup(int rt)
{
	tree[rt].sum=tree[lson].sum+tree[rson].sum;
}
void pushdown(int rt,int l,int r)
{
	tree[lson].sum+=(mid-l+1)*tree[rt].flag;
	tree[rson].sum+=(r-mid)*tree[rt].flag;
	tree[lson].flag+=tree[rt].flag;
	tree[rson].flag+=tree[rt].flag;
	tree[rt].flag=0;
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		tree[rt].sum=vl[l];
		return ;
	}
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(rt);
}
void ins(int rt,int l,int r,int L,int R,ll x)
{
	if(l==L&&r==R)
	{
		tree[rt].sum+=(r-l+1)*x;
		tree[rt].flag+=x;
		return ;
	}
	pushdown(rt,l,r);
	if(mid<L)
		ins(rson,mid+1,r,L,R,x);
	else if(mid>=R)
		ins(lson,l,mid,L,R,x);
	else
		ins(lson,l,mid,L,mid,x),ins(rson,mid+1,r,mid+1,R,x);
	pushup(rt);
}
ll query(int rt,int l,int r,int L,int R)
{
	if(l==L&&r==R)
		return tree[rt].sum;
	pushdown(rt,l,r);
	if(R<=mid)
		return query(lson,l,mid,L,R);
	else if(mid<L)
		return query(rson,mid+1,r,L,R);
	else
		return query(lson,l,mid,L,mid)+query(rson,mid+1,r,mid+1,R);
}
ll getans(int x)
{
	ll ans=0;
	while(x)
	{
		ans+=query(1,1,n,zhe[top[x]],zhe[x]);
		x=fa[top[x]];
	}
	return ans;
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld",&v[i]);
	for(int i=1;i<n;i++)
		scanf("%d%d",&a,&b),add(a,b),add(b,a);
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&a);
		if(a==1)
			scanf("%d%lld",&b,&c),ins(1,1,n,zhe[b],zhe[b],c);
		else if(a==2)
			scanf("%d%lld",&b,&c),ins(1,1,n,zhe[b],mx[b],c);
		else
			scanf("%d",&b),printf("%lld\n",getans(b));
	}
}

bzoj3626
题意:给一棵树。每次询问区间[l,r]这些点与z的sigma(dep[lca(i,z)]),l<=i<=r.
这个题实在是太吼了!

清华大佬gconeice的题解:

显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。(摘自 hzwer.com)

实在是太妙了啊啊啊在下不得不orz

#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<vector>
#define lson rt*2
#define rson rt*2+1
#define mid (l+r)/2
using namespace std;
const int N=50050;
const int mo=201314;
int ans[N];
int n,q,num;
int l,r,z;
struct state
{
	int loc,zz;
};
vector<state> jan[N],jia[N];
int head[N],to[N],next[N],cnt;
void add(int f,int t)
{
	to[++cnt]=t;
	next[cnt]=head[f];
	head[f]=cnt;
}
int fa[N];
int size[N],son[N],zhe[N],dui[N],top[N];
void dfs1(int x)
{
	size[x]=1;
	int flag=0;
	for(int i=head[x];i!=-1;i=next[i])
	{
		dfs1(to[i]);
		size[x]+=size[to[i]];
		if(size[to[i]]>flag)
		{
			flag=size[to[i]];
			son[x]=to[i];
		}
	}
}
void dfs2(int x,int root)
{
	zhe[x]=++num;
	dui[num]=x;
	top[x]=root;
	if(son[x])
		dfs2(son[x],root);
	for(int i=head[x];i!=-1;i=next[i])
		if(to[i]!=son[x])
			dfs2(to[i],to[i]);
}
struct node
{
	int sum;
	int flag;
}tree[N*4];
void pushup(int rt)
{
	tree[rt].sum=(tree[lson].sum+tree[rson].sum)%mo;
}
void pushdown(int rt,int l,int r)
{
	if(!tree[rt].flag)
		return ;
	tree[lson].flag+=tree[rt].flag;
	tree[rson].flag+=tree[rt].flag;
	tree[lson].sum=(tree[lson].sum+(tree[rt].flag*(mid-l+1))%mo)%mo;
	tree[rson].sum=(tree[rson].sum+(tree[rt].flag*(r-mid))%mo)%mo;
	tree[rt].flag=0;
}
void ins(int rt,int l,int r,int L,int R)
{
	if(l==L&&r==R)
	{
		tree[rt].sum=(tree[rt].sum+(r-l+1))%mo;
		tree[rt].flag+=1;
		return ;
	}
	pushdown(rt,l,r);
	if(mid>=R)
		ins(lson,l,mid,L,R);
	else if(mid<L)
		ins(rson,mid+1,r,L,R);
	else 
		ins(lson,l,mid,L,mid),ins(rson,mid+1,r,mid+1,R);
	pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
	if(l==L&&r==R)
		return tree[rt].sum;
	pushdown(rt,l,r);
	if(mid>=R)
		return query(lson,l,mid,L,R)%mo;
	else if(mid<L)
		return query(rson,mid+1,r,L,R)%mo;
	else
		return (query(lson,l,mid,L,mid)+query(rson,mid+1,r,mid+1,R))%mo;
}
void chang(int x)
{
	int d=x;
	while(d)
	{
		ins(1,1,n,zhe[top[d]],zhe[d]);
		d=fa[top[d]];
	}
}
int get(int x)
{
	int res=0;
	int d=x;
	while(d)
	{
		res=(res+query(1,1,n,zhe[top[d]],zhe[d])%mo)%mo;
		d=fa[top[d]];
	}
	return res;
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&q);
	for(int i=2;i<=n;i++)
		scanf("%d",&fa[i]),fa[i]++,add(fa[i],i);
	for(int i=1;i<=q;i++)
		scanf("%d%d%d",&l,&r,&z),jia[r+1].push_back((state){i,z+1}),jan[l].push_back((state){i,z+1});
	dfs1(1);
	dfs2(1,1);
	for(int i=1;i<=n;i++)
	{
		chang(i);
		for(int j=0;j<jan[i].size();j++)
			ans[jan[i][j].loc]=(ans[jan[i][j].loc]-get(jan[i][j].zz)+mo)%mo;
		for(int j=0;j<jia[i].size();j++)
			ans[jia[i][j].loc]=(get(jia[i][j].zz)+ans[jia[i][j].loc])%mo;
	}
	for(int i=1;i<=q;i++)
		printf("%d\n",ans[i]);
}

bzoj3531

题意:

一棵树,每个树有一种颜色和一个权值,现在有四种操作:

1.改变一个节点的权值

2.改变一个节点的颜色

3.询问从u到v(保证u,v颜色相同)这条路上与uv颜色相同的点的权值总和

4.询问从u到v(也保证u,v颜色相同)这条路上与uv颜色相同的点的权值最大值

要点:

1.怎么判断是不是颜色相同?

我学会了特殊的开多个线段树的技巧,我会假装四处看风景!

好吧不是什么特殊的,就是这个题需要为每个宗教开一个线段树,而我终于掌握了这种技巧。。。

#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<stdio.h>
#define mid (l+r)/2
using namespace std;
const int N=100050;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,a,b;
int head[N],to[2*N],next[2*N],cnt;
void add(int f,int t)
{
	to[++cnt]=t;
	next[cnt]=head[f];
	head[f]=cnt;
}
int s[N];
int T[N],w[N];
int fa[N][25];
int son[N],size[N];
int dep[N];
void dfs1(int x)
{
	dep[x]=dep[fa[x][0]]+1;
	for(int i=1;i<=16;i++)
	{
		if(s[i]>dep[x])
			break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	size[x]=1;
	int flag=0;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(to[i]==fa[x][0])
			continue;
		fa[to[i]][0]=x;	
		dfs1(to[i]);
		size[x]+=size[to[i]];
		if(size[to[i]]>flag)
			flag=size[to[i]],son[x]=to[i];
	}
}
int zhe[N],top[N],num;
void dfs2(int x,int root)
{
	zhe[x]=++num;
	top[x]=root;
	if(son[x])
		dfs2(son[x],root);
	for(int i=head[x];i!=-1;i=next[i])
		if(to[i]!=son[x]&&to[i]!=fa[x][0])
			dfs2(to[i],to[i]);
}
int lca(int u,int v)
{
	if(dep[u]<dep[v])
		swap(u,v);
	int cha=dep[u]-dep[v];
	for(int i=16;i>=0;i--)
		if(cha&s[i])
			u=fa[u][i];
	if(u==v)
		return u;
	for(int i=16;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
struct node
{
	int sum,mx,ls,rs;
}tree[100*N];
void pushup(int rt)
{
	tree[rt].sum=tree[tree[rt].ls].sum+tree[tree[rt].rs].sum;
	tree[rt].mx=max(tree[tree[rt].ls].mx,tree[tree[rt].rs].mx);
}
int tot;
int root[N];
void ins(int &rt,int l,int r,int pos,int val)
{
	if(!rt)
		rt=++tot;
	if(l==r)
	{
		tree[rt].mx=tree[rt].sum=val;
		return ;
	}
	if(pos<=mid)
		ins(tree[rt].ls,l,mid,pos,val);
	else
		ins(tree[rt].rs,mid+1,r,pos,val);
	pushup(rt);
}
int querysum(int rt,int l,int r,int L,int R)
{
	if(!rt)	return 0;
	if(l==L&&r==R)
		return tree[rt].sum;
	if(mid>=R)
		return querysum(tree[rt].ls,l,mid,L,R);
	else if(mid<L)
		return querysum(tree[rt].rs,mid+1,r,L,R);
	else 
		return querysum(tree[rt].ls,l,mid,L,mid)+querysum(tree[rt].rs,mid+1,r,mid+1,R);
}
int querymax(int rt,int l,int r,int L,int R)
{
	if(!rt) return 0;
	if(l==L&&r==R)
		return tree[rt].mx;
	if(mid>=R)
		return querymax(tree[rt].ls,l,mid,L,R);
	else if(mid<L)
		return querymax(tree[rt].rs,mid+1,r,L,R);
	else 
		return max(querymax(tree[rt].ls,l,mid,L,mid),querymax(tree[rt].rs,mid+1,r,mid+1,R));
}
int solvesum(int u,int v,int c)
{
	int res=0;
	int x=u;
	while(top[x]!=top[v])
	{
		res+=querysum(root[c],1,n,zhe[top[x]],zhe[x]);
		x=fa[top[x]][0];
	}
	res+=querysum(root[c],1,n,zhe[v],zhe[x]);
	return res;
}
int solvemax(int u,int v,int c)
{
	int res=0;
	int x=u;
	while(top[x]!=top[v])
	{
		res=max(res,querymax(root[c],1,n,zhe[top[x]],zhe[x]));
		x=fa[top[x]][0];
	}
	res=max(res,querymax(root[c],1,n,zhe[v],zhe[x]));
	return res;
}
int city1,city2,city,zong;
int main()
{
	s[0]=1;for(int i=1;i<=16;i++)s[i]=(s[i-1]<<1);
	memset(head,-1,sizeof(head));
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
		w[i]=read(),T[i]=read();
	for(int i=1;i<n;i++)
		a=read(),b=read(),add(a,b),add(b,a);
	dfs1(1);
	dfs2(1,1);
	for(int i=1;i<=n;i++)
		ins(root[T[i]],1,n,zhe[i],w[i]);
	for(int i=1;i<=m;i++)
	{
		char cc[4];
		scanf("%s",cc);
		if(cc[0]=='C')
		{
			if(cc[1]=='C')
			{
				city=read();
				zong=read();
				ins(root[T[city]],1,n,zhe[city],0);
				T[city]=zong;
				ins(root[T[city]],1,n,zhe[city],w[city]);
			}
			else
			{
				city=read();
				zong=read();
				ins(root[T[city]],1,n,zhe[city],zong);
				w[city]=zong;
			}
		}
		else
		{
			if(cc[1]=='S')
			{
				city1=read();
				city2=read();
				int f=lca(city1,city2);
				printf("%d\n",solvesum(city1,f,T[city1])+solvesum(city2,f,T[city2])-(T[f]==T[city1])*w[f]);
			}
			else if(cc[1]=='M')
			{
				city1=read();
				city2=read();
				int f=lca(city1,city2);
				printf("%d\n",max(solvemax(city1,f,T[city1]),solvemax(city2,f,T[city2])));
			}
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值