题意:给一棵树,三种操作。将第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\n",lca(x,y)); } } return 0; }