POJ3237 Tree 树链剖分 线段树
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - POJ3237
题意概括
Description
给你由N个结点组成的树。树的节点被编号为1到N,边被编号为1到N-1。每一条边有一个权值。然后你要在树上执行一系列指令。指令可以是如下三种之一:
CHANGE i v:将第i条边的权值改成v。
NEGATE a b:将点a到点b路径上所有边的权值变成其相反数。
QUERY a b:找出点a到点b路径上各边的最大权值。
Input
多组数据,数据为T<=20,对于每组数据:
第一行有一个整数N(N<=10000)。
接下来N-1行每行有三个整数a,b,c,代表点a和点b之间有一条权值为c的边。这些边按照其编号从小到大给出。
接下来是若干条指令(不超过10^5条),都按照上面所说的格式。
最后一行是"DONE".
Output
对每个“QUERY”指令,输出一行,即路径上各边的最大权值。
Sample Input
1
3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE
Sample Output
1
3
题解
树链剖分+线段树单点修改、区间修改、区间询问。
对于取相反数的,我们只需要维护一个最大值,一个最小值,然后打一下懒标记就可以了。
在下传的时候,最大值反一反一定是最小的,最小值反一反一定是最大的,然后就好办了。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdio> #include <cstdlib> using namespace std; const int N=10005; struct Edge{ int cnt,y[N*2],z[N*2],nxt[N*2],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt; } }g; struct edge{ int a,b,c; }e[N]; int T,n,fa[N],size[N],fadis[N],depth[N],son[N],top[N],p[N],ap[N],cnp; struct Tree{ int ma,mi,add; }t[N*4]; void Get_Gen_Info(int rt,int pre,int d){ depth[rt]=d,fa[rt]=pre,size[rt]=1,son[rt]=-1; for (int i=g.fst[rt];i;i=g.nxt[i]) if (g.y[i]!=pre){ int s=g.y[i]; Get_Gen_Info(s,rt,d+1); fadis[s]=g.z[i]; size[rt]+=size[s]; if (son[rt]==-1||size[s]>size[son[rt]]) son[rt]=s; } } void Get_Pos(int rt,int tp){ top[rt]=tp,p[rt]=++cnp,ap[cnp]=rt; if (son[rt]==-1) return; else Get_Pos(son[rt],tp); for (int i=g.fst[rt];i;i=g.nxt[i]){ int s=g.y[i]; if (s!=fa[rt]&&s!=son[rt]) Get_Pos(s,s); } } void pushup(int rt){ int ls=rt<<1,rs=ls|1; t[rt].mi=min(t[ls].mi,t[rs].mi); t[rt].ma=max(t[ls].ma,t[rs].ma); } void build(int rt,int le,int ri){ t[rt].add=0; if (le==ri){ t[rt].ma=t[rt].mi=fadis[ap[le]]; return; } int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; build(ls,le,mid); build(rs,mid+1,ri); pushup(rt); } void fz(int &x,int &y){ x=-x,y=-y; swap(x,y); } void pushdown(int rt){ int &a=t[rt].add; int ls=rt<<1,rs=ls|1; if (a==0) return; t[ls].add^=1,t[rs].add^=1; fz(t[ls].ma,t[ls].mi); fz(t[rs].ma,t[rs].mi); a=0; } void change(int rt,int le,int ri,int pos,int v){ if (le==ri){ t[rt].mi=t[rt].ma=v; return; } pushdown(rt); int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; if (pos<=mid) change(ls,le,mid,pos,v); else change(rs,mid+1,ri,pos,v); pushup(rt); } void update(int rt,int le,int ri,int xle,int xri){ if (le>xri||ri<xle) return; if (xle<=le&&ri<=xri){ t[rt].add^=1; fz(t[rt].ma,t[rt].mi); return; } pushdown(rt); int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; update(ls,le,mid,xle,xri); update(rs,mid+1,ri,xle,xri); pushup(rt); } int query(int rt,int le,int ri,int xle,int xri){ if (le>xri||ri<xle) return -1e9; if (xle<=le&&ri<=xri) return t[rt].ma; pushdown(rt); int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; return max(query(ls,le,mid,xle,xri),query(rs,mid+1,ri,xle,xri)); } void fupdate(int a,int b){ int f1=top[a],f2=top[b]; while (f1!=f2){ if (depth[f1]<depth[f2]) swap(f1,f2),swap(a,b); update(1,1,n,p[f1],p[a]); a=fa[f1],f1=top[a]; } if (a==b) return; if (depth[a]>depth[b]) swap(a,b); update(1,1,n,p[son[a]],p[b]); } int find(int a,int b){ int f1=top[a],f2=top[b],ans=-1e9; while (f1!=f2){ if (depth[f1]<depth[f2]) swap(f1,f2),swap(a,b); ans=max(ans,query(1,1,n,p[f1],p[a])); a=fa[f1],f1=top[a]; } if (a==b) return ans; if (depth[a]>depth[b]) swap(a,b); return max(ans,query(1,1,n,p[son[a]],p[b])); } int main(){ scanf("%d",&T); while (T--){ scanf("%d",&n); g.clear(); for (int i=1,a,b,c;i<n;i++){ scanf("%d%d%d",&a,&b,&c); g.add(a,b,c); g.add(b,a,c); e[i].a=a,e[i].b=b,e[i].c=c; } cnp=0,fadis[1]=0; Get_Gen_Info(1,0,0); Get_Pos(1,1); build(1,1,n); for (int i=1;i<n;i++) if (depth[e[i].a]>depth[e[i].b]) swap(e[i].a,e[i].b); char str[10]; int a,b; while (scanf("%s",str)&&str[0]!='D'){ scanf("%d%d",&a,&b); if (str[0]=='C') change(1,1,n,p[e[a].b],b); else if (str[0]=='N') fupdate(a,b); else printf("%d\n",find(a,b)); } } return 0; }