poj 3237(树链剖分+线段树)

题意:给一棵树,三种操作。将第i条边的权值改为v,将a到b的路径上的边的权值全部取反,求a到b路径上边的权值的最大值。

思路:明显的树链剖分,加上线段树的操作。因为有取反的操作所以每个区间要记录最大值和最小值。查询两点间的路径时,用求公共祖先的方式去求。





#include
#include
#include
const int N=101000;
const int inf=0x3fffffff;
using namespace std;
int head[N],num,son[N],sz[N],father[N],dep[N],idx,a[N],cot[N],ti[N],top[N];
struct edge
{
	int st,ed,w,next;
}e[N*4];
void addedge(int x,int y,int w)
{
	e[num].st=x;e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;
	e[num].st=y;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;
}
int max(int a,int b)
{
	if(a>b)return a;
	return b;
}
int min(int a,int b)
{
	if(a>b)return b;
	return a;
}
//******************树链剖分****************************
void find_son(int u,int fa)
{
	int i,v;
	son[u]=0;sz[u]=1;
	for(i=head[u];i!=-1;i=e[i].next)
	{
		v=e[i].ed;
		if(v==fa)continue;
		father[v]=u;
		dep[v]=dep[u]+1;
		a[v]=e[i].w;
		find_son(v,u);
		sz[u]+=sz[v];
		if(sz[v]>sz[son[u]])son[u]=v;
	}
}
void find_time(int u,int fa)
{
	int i,v;
	ti[u]=idx++;
	cot[ti[u]]=a[u];
	top[u]=fa;
	if(son[u]!=0)find_time(son[u],top[u]);
	for(i=head[u];i!=-1;i=e[i].next)
	{
		v=e[i].ed;
		if(v==son[u]||v==father[u])continue;
		find_time(v,v);
	}
}
//***********************线段树*********************
struct Tree
{
	int L,R,Mw,mw;
	int flag;//该区间是否取反
}T[N*10];
void up(int id)
{
	int li=id<<1,ri=li|1;
    T[id].Mw=max(T[li].Mw,T[ri].Mw);
	T[id].mw=min(T[li].mw,T[ri].mw);
}
void buildTree(int L,int R,int id)
{
	T[id].L=L;T[id].R=R;T[id].flag=0;
	if(L==R)
	{
		T[id].Mw=T[id].mw=cot[L];
		return;
	}
	int mid=(L+R)>>1,li=id<<1,ri=li|1;
	buildTree(L,mid,li);
	buildTree(mid+1,R,ri);
	up(id);
}
void fan(int id)
{
	if(T[id].L==T[id].R)return ;
	int li=id<<1,ri=li|1;
	T[id].flag=0;//传递给两个子区间后该区间上不取反
	T[li].flag^=1;T[ri].flag^=1;
	T[li].Mw*=-1;T[li].mw*=-1;
	T[ri].Mw*=-1;T[ri].mw*=-1;
	swap(T[li].Mw,T[li].mw);
	swap(T[ri].Mw,T[ri].mw);
}
void Negate(int L,int R,int id)//取反操作
{
	if(T[id].L==L&&T[id].R==R)
	{
		T[id].Mw*=-1;
		T[id].mw*=-1;
		swap(T[id].Mw,T[id].mw);
		T[id].flag^=1;
		return ;
	}
	int mid=(T[id].L+T[id].R)>>1,li=id<<1,ri=li|1;
	if(T[id].flag)
	{
		fan(id);
	}
	if(R<=mid)Negate(L,R,li);
	else if(L>mid)Negate(L,R,ri);
	else 
	{
		Negate(L,mid,li);
		Negate(mid+1,R,ri);
	}
	up(id);
}
void insert(int x,int id,int w)//更新x的值
{
	if(T[id].L==x&&T[id].R==x)
	{
		T[id].Mw=T[id].mw=w;
		T[id].flag=0;
		return ;
	}	
	int mid=(T[id].L+T[id].R)>>1,li=id<<1,ri=li|1;
	if(T[id].flag)
		fan(id);
	if(x<=mid)insert(x,li,w);
	else insert(x,ri,w);
	up(id);
}
int find(int L,int R,int id)//找最大值
{
	if(T[id].L==L&&T[id].R==R)
	{
		return T[id].Mw;
	}
	int mid=(T[id].L+T[id].R)>>1,li=id<<1,ri=li|1;
	if(T[id].flag)
		fan(id);
	if(R<=mid)return find(L,R,li);
	else if(L>mid)return find(L,R,ri);
	else return max(find(L,mid,li),find(mid+1,R,ri));
	up(id);
}
int lca(int x,int y)//x到y路径上的最大值
{
	int ans=-inf;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]dep[y])swap(x,y);
	if(x!=y)
		ans=max(ans,find(ti[x]+1,ti[y],1));
	return ans;
}
void LCA(int x,int y)//x到y路径权值取反
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]dep[y])swap(x,y);
	if(x!=y)
		Negate(ti[x]+1,ti[y],1);//ti[x]是x与父节点的边
}
int main()
{
	int i,n,t,x,y,w;
	char str[10];
	scanf("%d",&t);
	while(t--)
	{
		memset(head,-1,sizeof(head));
		num=0;
		scanf("%d",&n);
		for(i=1;idep[e[x].ed])//每条边的取值在度数大的点上
					swap(e[x].st,e[x].ed);
				insert(ti[e[x].ed],1,y);
			}
			else if(str[0]=='N')
				LCA(x,y);
			else 
				printf("%d\n",lca(x,y));
		}
	}
	return 0;
}


你可能感兴趣的:(字典树-线段树-划分树,数据结构)