POJ 3237(树链剖分)

本文详细解析了一道树链剖分模板题的解题思路和难点,通过实例展示了如何将两百多行代码敲出AC结果,并在区间取反部分注意细节。介绍了树链剖分的基本概念、代码实现以及优化技巧。

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

这道题算是树链剖分的模板题吧(其实也是我过的第一道树链剖分的题目)。历尽千辛万苦,终于把两百多行的代码敲出来了,ac的一瞬间还是挺爽的。

在学会了树链剖分之后,这道题不算很难的了,主要是在区间取反的部分注意一下细节。树链剖分代码挺长的,很多地方都是容易写错。我也是通过讨论区的acmer给出的数据找出一些错误的地方。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
#include<map>
#include<queue>
#include<climits>
#include<assert.h>
using namespace std;

//树链剖分
const int maxn = 10005;
int dep[maxn],siz[maxn],fa[maxn],id[maxn],son[maxn],num[maxn],top[maxn];
int fir[maxn],vv[maxn<<1],val[maxn<<1],nxt[maxn<<1],e;
int ll[maxn<<2],rr[maxn<<2],maxed[maxn<<2],minn[maxn<<2];
bool negative[maxn<<2];
int idx;

struct Edge
{
	int from,to,val;
	Edge(int f,int t,int v)
		:from(f),to(t),val(v){}
	Edge(){}
};
vector<Edge>vec;

void add(int a,int b,int v)
{
	vv[e]=b;
	val[e]=v;
	nxt[e]=fir[a];
	fir[a]=e++;
}

void dfs(int cur,int pre)
{
	siz[cur]=1;son[cur]=-1;
	int maxed=-1,sonn=-1;
	for(int i=fir[cur];i!=-1;i=nxt[i])
	{
		int v=vv[i];
		if(v==pre) continue;
		dep[v]=dep[cur]+1;
		fa[v]=cur;
		dfs(v,cur);
		if(siz[v]>maxed)
		{
			maxed=siz[v];
			sonn=v;
		}
		siz[cur]+=siz[v];
	}
	son[cur]=sonn;
}

void dfs2(int cur,int tp)
{
	top[cur]=tp;
	id[cur]=++idx;
	if(son[cur]!=-1) dfs2(son[cur],tp);
	for(int i=fir[cur];i!=-1;i=nxt[i])
	{
		int v=vv[i];
		if(v==fa[cur]||v==son[cur]) continue;
		dfs2(v,v);
	}
}

void push_down(int i)
{
	if(negative[i])
	{
		int ls=i<<1,rs=ls|1;
		negative[ls]=!negative[ls];
		negative[rs]=!negative[rs];
		int temp=maxed[ls];//要用中间变量记录
		maxed[ls]=-minn[ls];
		minn[ls]=-temp;
		temp=maxed[rs];
		maxed[rs]=-minn[rs];
		minn[rs]=-temp;
		negative[i]=false;
	}
}

void build(int i,int l,int r)
{
	ll[i]=l,rr[i]=r,negative[i]=false;
	if(l==r) 
	{
		maxed[i]=num[l];
		minn[i]=num[l];
		return ;
	}
	int mid=(l+r)>>1,ls=i<<1,rs=ls|1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	maxed[i]=max(maxed[ls],maxed[rs]);
	minn[i]=min(minn[ls],minn[rs]);
}

void updatePos(int i,int pos,int _val)
{
	if(ll[i]==rr[i]) 
	{
		maxed[i]=_val;
		minn[i]=_val;
		return;
	}
	push_down(i);//不能漏掉这一句
	int mid=(ll[i]+rr[i])>>1,ls=i<<1,rs=ls|1;
	if(pos<=mid) updatePos(ls,pos,_val);
	else updatePos(rs,pos,_val);
	maxed[i]=max(maxed[ls],maxed[rs]);
	minn[i]=min(minn[ls],minn[rs]);
}

void negaSeg(int i,int l,int r)
{
	if(ll[i]>=l&&rr[i]<=r)
	{
		negative[i]=!negative[i];
		int temp=minn[i];
		minn[i]=-maxed[i];
		maxed[i]=-temp;
		return;
	}
	push_down(i);
	int mid=(ll[i]+rr[i])>>1,ls=i<<1,rs=ls|1;
	if(l>mid) negaSeg(rs,l,r);
	else if(r<=mid) negaSeg(ls,l,r);
	else 
	{
		negaSeg(ls,l,r);
		negaSeg(rs,l,r);
	}
	maxed[i]=max(maxed[ls],maxed[rs]);
	minn[i]=min(minn[ls],minn[rs]);
}

int query_max(int i,int l,int r)
{
	if(ll[i]>=l&&rr[i]<=r) 
		return maxed[i]; 
	push_down(i);
	int mid=(ll[i]+rr[i])>>1,ls=i<<1,rs=ls|1;
	if(l>mid) return query_max(rs,l,r);
	else if(r<=mid) return query_max(ls,l,r);
	else return max(query_max(ls,l,r),query_max(rs,l,r));
}

int query_min(int i,int l,int r)
{
	if(ll[i]>=l&&rr[i]<=r) 
		return minn[i];
	push_down(i);
	int mid=(ll[i]+rr[i])>>1,ls=i<<1,rs=ls|1;
	if(l>mid) return query_min(rs,l,r);
	else if(r<=mid) return query_min(ls,l,r);
	else return min(query_min(ls,l,r),query_min(rs,l,r));
}

//initialize the upper edge of one node
void init()
{
	for(int i=0;i<vec.size();i++)
	{
		Edge cur=vec[i];
		int high=cur.from,low=cur.to;
		if(dep[high]<dep[low]) swap(high,low);
		num[id[high]]=cur.val;
	}
}

int query_tree(int u,int v)
{
	int tp1=top[u],tp2=top[v];
	int ans=-(INT_MAX/2);
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		ans=max(query_max(1,id[tp1],id[u]),ans);
		u=fa[tp1];
		tp1=top[u];
	}
	if(u==v) return ans;
	if(dep[u]>dep[v]) swap(u,v);
	ans=max(ans,query_max(1,id[son[u]],id[v]));
	return ans;
}

void nega_tree(int u,int v)
{
	int tp1=top[u],tp2=top[v];
	while(tp1!=tp2)
	{
		if(dep[tp1]<dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		negaSeg(1,id[tp1],id[u]);
		u=fa[tp1];tp1=top[u];
	}
	if(u==v) return;
	if(dep[u]>dep[v]) swap(u,v);
	negaSeg(1,id[son[u]],id[v]);
}


int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)fir[i]=-1,num[i]=0;
		e=0;idx=0;
		vec.clear();

		int a,b,v;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d%d",&a,&b,&v);
			add(a,b,v);
			add(b,a,v);
			vec.push_back(Edge(a,b,v));//这一句不能在add函数里面写
		}

		dep[1]=0;
		dfs(1,1);
		dfs2(1,1);
		init();
		build(1,1,n);

		char ins[10];
		while(scanf("%s",ins)!=EOF)
		{
			if(ins[0]=='D') break;
			if(ins[0]=='C')
			{
				scanf("%d%d",&a,&b);
				Edge cur=vec[a-1];
				int from=cur.from,to=cur.to;
				if(dep[from]<dep[to]) swap(from,to);
				updatePos(1,id[from],b);
			}
			else if(ins[0]=='N')
			{
				scanf("%d%d",&a,&b);
				nega_tree(a,b);
			}
			else if(ins[0]=='Q')
			{
				scanf("%d%d",&a,&b);
				printf("%d\n",query_tree(a,b));
			}
		}
	}
}

/*
1000
6
1 2 1
2 3 2
3 4 4
4 5 100
5 6 -1000
CHANGE 2 20
QUERY 1 3
NEGATE 1 2
QUERY 1 3
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值