POJ 3237 Tree
题目大意:
给你n个结点的树,有三种操作:
1.CHANGE i v 将i号边边权变为v
2.NEGATE a b 将a点到b点路径上的边权取相反数
3.QUERY a b 找到a点到b点路径上的边权的最大值
输出所有3操作结果,指令结束标志为”DONE”.
有多组数据.
题目分析:
(又滚去做树链剖分的题,元旦放假前开始做到现在,233)
将边权转化成相连两点中子节点的点权,根无权值.
第一个操作,单点修改;第三个操作,区间最值.
主要是第二个操作,区间取相反数.
维护区间的max和min,便于取反操作.
对于每一个取反操作,区间加标记,交换max和min的值,并且max,min取其相反数.
但是需要注意,线段树的所有操作都要考虑标记的下放,之前没注意这个,WA了.
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
const int maxm=20000+10;
const int INF=(1<<30);
int fir[maxn],nxt[maxm],to[maxm],ecnt;
void add_edge(int u,int v)
{
nxt[++ecnt]=fir[u];fir[u]=ecnt;to[ecnt]=v;
nxt[++ecnt]=fir[v];fir[v]=ecnt;to[ecnt]=u;
}
int fa[maxn],sz[maxn],son[maxn];
void dfs1(int u,int p)
{
fa[u]=p;sz[u]=1;son[u]=0;
for(int i=fir[u];i;i=nxt[i]) if(to[i]!=p) {
int v=to[i];
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
int top[maxn],dep[maxn],idx[maxn],id;
void dfs2(int u,int t,int d)
{
top[u]=t;dep[u]=d;idx[u]=++id;
if(son[u]) dfs2(son[u],t,d+1);
for(int i=fir[u];i;i=nxt[i])
if(to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i],d+1);
}
struct Edge {
int u,v,w;
Edge(){}
Edge(int u,int v,int w):u(u),v(v),w(w){}
}tmp[maxn];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
int val[maxn],maxs[maxn<<2],mins[maxn<<2],tag[maxn<<2];
void negat(int x)//取反操作
{
tag[x]^=1;
swap(maxs[x],mins[x]);
maxs[x]*=-1;mins[x]*=-1;
}
void put_down(int x)//标记下方
{
if(!tag[x]) return ;
negat(lc);negat(rc);
tag[x]=0;
}
void put_up(int x)
{
maxs[x]=max(maxs[lc],maxs[rc]);
mins[x]=min(mins[lc],mins[rc]);
}
void build(int x,int l,int r)
{
if(l==r) maxs[x]=mins[x]=val[l];
else {
build(lc,l,mid);
build(rc,mid+1,r);
put_up(x);
}
}
void change(int x,int l,int r,int q,int v)//单点修改
{
if(l==r&&l==q) {
maxs[x]=mins[x]=v;
return ;
}
put_down(x);//单点修改时也应将标记下放
if(q<=mid) change(lc,l,mid,q,v);
else change(rc,mid+1,r,q,v);
put_up(x);
}
void update(int x,int l,int r,int ql,int qr)//区间取反
{
if(ql<=l&&r<=qr) {
negat(x);
return ;
}
put_down(x);
if(ql<=mid) update(lc,l,mid,ql,qr);
if(qr>mid) update(rc,mid+1,r,ql,qr);
put_up(x);
}
int query(int x,int l,int r,int ql,int qr)//区间最值
{
if(ql<=l&&r<=qr) return maxs[x];
int L=-INF,R=L;
put_down(x);
if(ql<=mid) L=query(lc,l,mid,ql,qr);
if(qr>mid) R=query(rc,mid+1,r,ql,qr);
return max(L,R);
}
int n;
void solve(int x,int y,int k)//k 0 NEGATE 1 QUERY
{
int ret=-INF;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(k) ret=max(ret,query(1,2,n,idx[top[x]],idx[x]));
else update(1,2,n,idx[top[x]],idx[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
if(idx[y]>idx[x]) {
if(k) ret=max(ret,query(1,2,n,idx[x]+1,idx[y]));
else update(1,2,n,idx[x]+1,idx[y]);
}
if(k) printf("%d\n",ret);
}
void init()//多组数据记得初始化
{
ecnt=id=0;
memset(fir,0,sizeof(fir));
memset(tag,0,sizeof(tag));
}
char op[10];
int main()
{
int T,a,b;
scanf("%d",&T);
while(T--) {
init();
scanf("%d",&n);
for(int u,v,w,i=1;i<n;i++) {
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v);
tmp[i]=Edge(u,v,w);
}
dfs1(1,1);
dfs2(1,1,1);
for(int i=1;i<n;i++) {//边权转化成点权
int u=tmp[i].u,v=tmp[i].v;
int f=dep[u]>dep[v]?u:v;
val[idx[f]]=tmp[i].w;
tmp[i].u=f;
}
build(1,2,n);
while(scanf("%s",op)==1&&op[0]!='D') {
scanf("%d%d",&a,&b);
if(op[0]=='C') change(1,2,n,idx[tmp[a].u],b);
else solve(a,b,op[0]=='N'?0:1);
}
}
return 0;
}