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


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

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





    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    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[top[y]])swap(x,y);
    		ans=max(ans,find(ti[top[x]],ti[x],1));
    		x=father[top[x]];
    	}
    	if(dep[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[top[y]])swap(x,y);
    		Negate(ti[top[x]],ti[x],1);
    		x=father[top[x]];
    	}
    	if(dep[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;i<n;i++)
    		{
    			scanf("%d%d%d",&x,&y,&w);
    			addedge(x,y,w);
    		}
    		dep[1]=1;
    		sz[0]=father[1]=0;idx=1;
    		find_son(1,0);
    		find_time(1,1);
    		buildTree(1,n,1);
    		while(scanf("%s",str),str[0]!='D')
    		{
    			scanf("%d%d",&x,&y);
    			if(str[0]=='C')
    			{
    				x=x*2-2;
    				if(dep[e[x].st]>dep[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
    ",lca(x,y));
    		}
    	}
    	return 0;
    }


  • 相关阅读:
    AlphaMobileControls
    .NET Compact Framework下注册表导出工具的开发
    windows moblie 5.0在托管程序中实现短信接收和拦截
    自定义MessageBox
    Windows Mobile 背景灯控制
    windows Mobile 启动Mobile Office
    windows mobile 5.0 进程管理、窗体管理、重启和关闭操作系统
    让Windows Mobile模拟器通过你的PC上网
    Windows Mobile获取存储卡容量及使用情况
    透明背景
  • 原文地址:https://www.cnblogs.com/james1207/p/3367904.html
Copyright © 2020-2023  润新知