bzoj3531: [Sdoi2014]旅行

本文介绍了一种使用树链剖分解决区间查询问题的方法,包括单点赋值、区间求和及区间求最值等操作。通过构建辅助数据结构,实现了高效的查询和更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

  http://www.lydsy.com/JudgeOnline/problem.php?id=3531

题解

  对于每一种宗教,把和这个宗教有关的点和操作单独在一棵树上搞。

  查询操作很容易,修改评级也很容易,改宗教的话,就是把一个点在原来的宗教的树上赋成0,在更改后的宗教的树上加上自己的权值。

  较水。。一开始数组开小了RE,改了就A了。2A。

代码

//树链剖分
//1 a b 单点赋值 
//2 a b 区间和
//3 a b 区间最值 
#include <cstdio>
#include <algorithm>
#define maxn 300000
#define inf 0x7fffffff
using namespace std;
int N, etot, head[maxn], to[maxn], next[maxn], totq, tid[maxn], deep[maxn], size[maxn],
	fa[maxn], son[maxn], first[maxn], last[maxn], c[maxn], w[maxn], val[maxn], 
	top[maxn], tim;
struct quiry{int a, b, type, next, ans;}q[maxn];
struct segtree
{
	int l, r, max, sum, set;
	segtree *lch, *rch;
	segtree(){l=r=sum=max=0;lch=rch=0;set=-1;max=-inf;}
}*root;
void adde(int a, int b){to[++etot]=b;next[etot]=head[a];head[a]=etot;}
void pushdown(segtree *p)
{
	if(p->set!=-1)
	{
		p->sum=(p->r-p->l+1)*p->set;
		p->max=p->set;
		if(p->lch)p->lch->set=p->rch->set=p->set;
		p->set=-1;
	}
}
void update(segtree *p)
{
	if(p->lch==0)return;
	pushdown(p->lch),pushdown(p->rch);
	p->sum=p->lch->sum+p->rch->sum;
	p->max=max(p->lch->max,p->rch->max);
}
void segset(segtree *p, int l, int r, int tag)
{
	pushdown(p);
	int mid=(p->l+p->r)>>1;
	if(l<=p->l and r>=p->r){p->set=tag;return;}
	if(l<=mid)segset(p->lch,l,r,tag);
	if(r>mid)segset(p->rch,l,r,tag);
	update(p);
}
int segmax(segtree *p, int l, int r)
{
	pushdown(p);
	int mid=(p->l+p->r)>>1, ans=-inf;
	if(l<=p->l and r>=p->r)return p->max;
	if(l<=mid)ans=max(ans,segmax(p->lch,l,r));
	if(r>mid)ans=max(ans,segmax(p->rch,l,r));
	return ans;
}
int segsum(segtree *p, int l, int r)
{
	pushdown(p);
	int mid=(p->l+p->r)>>1, ans=0;
	if(l<=p->l and r>=p->r)return p->sum;
	if(l<=mid)ans+=segsum(p->lch,l,r);
	if(r>mid)ans+=segsum(p->rch,l,r);
	return ans;
}
void build(segtree *p, int l, int r)
{
	int mid=(l+r)>>1;
	p->l=l,p->r=r;
	if(l==r){return;}
	build(p->lch=new segtree,l,mid);
	build(p->rch=new segtree,mid+1,r);
	update(p);
}
void dfs1(int pos)
{
	int p, v;
	size[pos]=1;
	for(p=head[pos];p;p=next[p])
	{
		if((v=to[p])==fa[pos])continue;
		deep[v]=deep[pos]+1;
		fa[v]=pos;
		dfs1(v);
		if(son[pos]==0 or size[v]>size[son[pos]])son[pos]=v;
		size[pos]+=size[v];
	}
}
void dfs2(int pos, int tp)
{
	int p, v;
	top[pos]=tp;
	tid[pos]=++tim;
	if(son[pos])dfs2(son[pos],tp);
	for(p=head[pos];p;p=next[p])
	{
		if((v=to[p])==fa[pos] or v==son[pos])continue;
		dfs2(v,v);
	}
}
int solve(int a, int b, int type)	//0:sum | 1:max
{
	int ta=top[a], tb=top[b], ans=type?-inf:0, x;
	while(ta!=tb)
	{
		if(deep[ta]<deep[tb])swap(ta,tb),swap(a,b);
		if(type)x=segmax(root,tid[ta],tid[a]),ans=max(ans,x);
		else x=segsum(root,tid[ta],tid[a]),ans+=x;
		a=fa[ta];ta=top[a];
	}
	if(deep[a]>deep[b])swap(a,b);
	if(type)x=segmax(root,tid[a],tid[b]),ans=max(ans,x);
	else ans+=segsum(root,tid[a],tid[b]);
	return ans;
}
void init()
{
	int i, Q, a, b;
	char s[10];
	scanf("%d%d",&N,&Q);
	for(i=1;i<=N;i++)
	{
		scanf("%d%d",&a,&b);
		w[i]=a;
		c[i]=b;
		++totq;
		if(first[b]==0)first[b]=last[b]=totq;
		else q[last[b]].next=totq,last[b]=totq;
		q[totq].type=1;q[totq].a=i;q[totq].b=a;
	}
	for(i=1;i<N;i++)scanf("%d%d",&a,&b),adde(a,b),adde(b,a);
	for(i=1;i<=Q;i++)
	{
		scanf("%s%d%d",s,&a,&b);
		++totq;
		q[totq].a=a;
		q[totq].b=b;
		q[last[c[a]]].next=totq;
		last[c[a]]=totq;
		if(s[1]=='C')
		{
			q[totq].type=1;
			q[totq].b=0;
			totq++;
			q[totq].type=1;
			q[last[b]].next=totq;
			last[b]=totq;
			q[totq].a=a;q[totq].b=w[a];
			c[a]=b;
		}
		if(s[1]=='W')q[totq].type=1,w[a]=b;
		if(s[1]=='S')q[totq].type=2;
		if(s[1]=='M')q[totq].type=3;
	}
}
void solve(int num)
{
	int pt, x, a, b;
	root->set=0;
	for(pt=first[num];pt;pt=q[pt].next)
	{
		x=q[pt].type;
		a=q[pt].a;
		b=q[pt].b;
		if(x==1)segset(root,tid[a],tid[a],b);
		if(x==2)q[pt].ans=solve(a,b,0);
		if(x==3)q[pt].ans=solve(a,b,1);
	}
}
void print()
{
	int i;
	for(i=1;i<=totq;i++)if(q[i].type!=1)printf("%d\n",q[i].ans);
}
int main()
{
	int i;
	init();
	dfs1(1);
	dfs2(1,1);
	build(root=new segtree,1,tim);
	for(i=1;i<=100000;i++)solve(i);
	print();
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值