BZOJ 3531 SDOI2014 旅行 树链剖分

题目大意:给定一棵树,每一个点有一个权值和一个颜色。多次改变一些点的权值和颜色,多次求一条路径上与起点和终点颜色同样的点的权值和以及权值最大值

每种颜色开一个线段树 动态开节点 每一个点仅仅建一条链 这样空间复杂度是O(nlogn)的

然后就正常树链剖分即可了

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct abcd{
	int to,next;
}table[M<<1];
struct Segtree{
	Segtree *ls,*rs;
	int sum,max_num;
	Segtree():ls(0x0),rs(0x0),sum(0),max_num(0){}
}*tree[M];
int head[M],tot;
int n,m;
int w[M],c[M];
int fa[M],son[M],dpt[M],size[M],pos[M],top[M],cnt;
void Update(Segtree* &p,int x,int y,int z,int v)
{
	int mid=x+y>>1;
	if(!p) p=new Segtree;
	if(x==y)
	{
		p->sum=p->max_num=v;
		return ;
	}
	if(z<=mid) Update(p->ls,x,mid,z,v);
	else       Update(p->rs,mid+1,y,z,v);
	p->sum=(p->ls?p->ls->sum:0)+(p->rs?p->rs->sum:0);
	p->max_num=max( (p->ls?p->ls->max_num:0) , (p->rs?

p->rs->max_num:0) );
}
int Get_Sum(Segtree *p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(!p) return 0;
	if(x==l&&y==r) return p->sum;
	if(r<=mid) return Get_Sum(p->ls,x,mid,l,r);
	if(l>mid) return Get_Sum(p->rs,mid+1,y,l,r);
	return Get_Sum(p->ls,x,mid,l,mid) + Get_Sum(p->rs,mid+1,y,mid+1,r);
}
int Get_Max(Segtree *p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(!p) return 0;
	if(x==l&&y==r) return p->max_num;
	if(r<=mid) return Get_Max(p->ls,x,mid,l,r);
	if(l>mid) return Get_Max(p->rs,mid+1,y,l,r);
	return max( Get_Max(p->ls,x,mid,l,mid) , Get_Max(p->rs,mid+1,y,mid+1,r) );
}
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
void DFS1(int x)
{
	int i;
	dpt[x]=dpt[fa[x]]+1;
	size[x]=1;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x])
			continue;
		fa[table[i].to]=x;
		DFS1(table[i].to);
		size[x]+=size[table[i].to];
		if(size[table[i].to]>size[son[x]])
			son[x]=table[i].to;
	}
}
void DFS2(int x)
{
	int i;
	pos[x]=++cnt;
	if(son[fa[x]]==x)
		top[x]=top[fa[x]];
	else
		top[x]=x;
	Update(tree[c[x]],1,n,pos[x],w[x]);
	if(son[x]) DFS2(son[x]);
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x]||table[i].to==son[x])
			continue;
		DFS2(table[i].to);
	}
}
int Query_Sum(int x,int y,int _c)
{
	int fx=top[x],fy=top[y],re=0;
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			swap(x,y),swap(fx,fy);
		re+=Get_Sum(tree[_c],1,n,pos[fx],pos[x]);
		x=fa[fx];fx=top[x];
	}
	if(dpt[x]<dpt[y])
		swap(x,y);
	re+=Get_Sum(tree[_c],1,n,pos[y],pos[x]);
	return re;
}
int Query_Max(int x,int y,int _c)
{
	int fx=top[x],fy=top[y],re=0;
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			swap(x,y),swap(fx,fy);
		re=max(re,Get_Max(tree[_c],1,n,pos[fx],pos[x]));
		x=fa[fx];fx=top[x];
	}
	if(dpt[x]<dpt[y])
		swap(x,y);
	re=max(re,Get_Max(tree[_c],1,n,pos[y],pos[x]));
	return re;
}
int main()
{
	int i,x,y;
	char p[10];
	cin>>n>>m;
	for(i=1;i<=n;i++)
		scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<n;i++)
		scanf("%d%d",&x,&y),Add(x,y),Add(y,x);
	DFS1(1);DFS2(1);
	for(i=1;i<=m;i++)
	{
		scanf("%s%d%d",p,&x,&y);
		switch(p[1])
		{
			case 'C':
				Update(tree[c[x]],1,n,pos[x],0);
				Update(tree[c[x]=y],1,n,pos[x],w[x]);
				break;
			case 'W':
				Update(tree[c[x]],1,n,pos[x],w[x]=y);
				break;
			case 'S':
				printf("%d\n", Query_Sum(x,y,c[x]) );
				break;
			case 'M':
				printf("%d\n", Query_Max(x,y,c[x]) );
				break;
		}
	}
}
时间: 2024-11-06 04:51:59

BZOJ 3531 SDOI2014 旅行 树链剖分的相关文章

BZOJ3531 SDOI2014 旅行 - 树链剖分,主席树

题意:给定一棵树,树上每个点有权值和类型.支持:修改某个点的类型:修改某个点的权值:询问某条链上某个类型的点的和/最大值.点数/类型数/询问数<=100000. 分析: 树链剖分,对每个类型的点建立线段树(动态开点). note: 忘记写t_query返回值调半天-- 莫名其妙地1A 代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int a[5000005],s[5000005],dep[100005],size[10

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

【BZOJ 3531】【SDOI 2014】旅行 树链剖分

因为有$10^5$个宗教,需要开$10^5$个线段树. 平时开的线段树是“满”二叉树,但在这个题中代表一个宗教的线段树管辖的区间有很多点都不属于这个宗教,也就不用“把枝叶伸到这个点上”,所以这样用类似主席树的数组动态开点来建立$10^5$个只有几个“树枝”的线段树,维护轻重链就可以了 线段树的$L,R,l,r$弄反了调了好久$QAQ$ $so$ $sad$ #include<cstdio> #include<cstring> #include<algorithm> #d

BZOJ 2157 旅行(树链剖分码农题)

写了5KB,1发AC... 题意:给出一颗树,支持5种操作. 1.修改某条边的权值.2.将u到v的经过的边的权值取负.3.求u到v的经过的边的权值总和.4.求u到v的经过的边的权值最大值.5.求u到v经过的边的权值最小值. 基于边权的树链剖分,放在线段树上变成了区间维护问题了,线段树维护4个量min,max,sum,tag就可以了. # include <cstdio> # include <cstring> # include <cstdlib> # include

BZOJ 2243 染色(树链剖分好题)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 7971  Solved: 2990 [Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依

[bzoj 2243]: [SDOI2011]染色 [树链剖分][线段树]

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边. 下面 行每行描述一个操作: “C a

BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LC

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写