BSOJ1125:树Tree 树链剖分 单点修改 区间取反 区间查询

1125 -- 【POJ3237】树Tree
Description
  给你一颗具有n个结点的树,结点编号为1到n且边的编号为1到n-1。每条边有一个边权。现要求模拟下列三种操作:
  1.CHANGE i v:修改第i条边的权值为v
  2.NEGATE a b:把a点到b点之间路径的边权全部取反;
  3.QUERY a b:询问a点到b点之间路径的最大边权;
Input
  第一行为一个整数N(N<=30000);
  接下来N-1行,每行包含三个整数a,b,c,表示a到b有一条边,边权为c。边的编号是按照输入的顺序。接下来是若干个询问,以一行一个单词“DONE”作为这组数据的结束。
Output
  对于每个“QUERY”输出一行。
Sample Input

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE
Sample Output
1
3

这道题涉及三个问题 1.单边(点)修改  2.区间取反 3.区间最大
区间最大,就是在Pushup维护的时候从儿子得到最大,输入时直接赋值就好了。
重点是第二问,区间取反。
 其实也没什么特别的,跟区间修改类似,一个递归+一个爬top[]就可以解决,记录lazy标记,下传给儿子。
*但
是取反之后,区间max怎么维护? 
一开始想的顺其自然,*=-1,上传维护,耶耶耶。
哦嚯,这样的想法是错误的。
 权值取相反数之后, 原maxx变为最小,原minn变为最大。
于是我们要维护一个minn来维护maxx. 
取反,minn和maxx*=-1,交换,即可维护maxx了,happy ending.

贴上代码,错误&易错注释在下
#include<iostream>
#include<cstdio>
#include<cstring>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define INF 0x7fffffff
using namespace std;
int fa[300005]={0},top[300005]={0},son[300005]={0},size[300005]={0};
int tid[300005]={0},dep[300005]={0};
struct SegmentTree
{
int l,r,minn,maxx,neg;
}tree[300005];
struct TREE
{
int x,y,v;
}t[300005]={0};
struct Edge
{
int to,next;
}w[300005]={0};
int tim=0;
int cnt=0;
int Max=-INF;
int h[300005]={0};
void add(int x,int y)
{
cnt++;w[cnt].to=y;w[cnt].next=h[x];h[x]=cnt;
}
void DFS1(int x,int f,int d)
{
dep[x]=d;
fa[x]=f;
size[x]=1;
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to!=f)
{
DFS1(to,x,d+1);
size[x]+=size[to];
if(son[x]==0||size[son[x]]<size[to])son[x]=to;
}
}
}
void DFS2(int x,int tp)
{
top[x]=tp;
tid[x]=++tim;
if(son[x]==0)return;
DFS2(son[x],tp);
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to!=son[x]&&to!=fa[x])DFS2(to,to);
}
}
void Pushup(int x)
{
tree[x].maxx=max(tree[L(x)].maxx,tree[R(x)].maxx);
tree[x].minn=min(tree[L(x)].minn,tree[R(x)].minn);
return;
}
void Pushdown(int x)
{
if(tree[x].neg)//取反懒惰标记下传,改变儿子以及他们的懒惰标记
{
int l=L(x),r=R(x);
tree[l].neg^=1;tree[r].neg^=1;
tree[l].maxx*=-1;tree[l].minn*=-1;
swap(tree[l].maxx,tree[l].minn);
tree[r].maxx*=-1;tree[r].minn*=-1;
swap(tree[r].maxx,tree[r].minn);
tree[x].neg=0;//注意清零
}
return;
}
void build(int l,int r,int root)
{
tree[root].l=l;tree[root].r=r;
tree[root].maxx=0;
tree[root].minn=0;
tree[root].neg=0;
if(l==r)return;
int mid=(l+r)>>1;
build(l,mid,L(root));
build(mid+1,r,R(root));
Pushup(root);
}
//递归首部下传底部上传,安全正确有保险。
void change(int x,int data,int root)
{
Pushdown(root);
if(tree[root].l==x&&tree[root].r==x)
{
tree[root].maxx=data;
tree[root].minn=data;
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(x<=mid)change(x,data,L(root));
else change(x,data,R(root));
Pushup(root);
}
void DoNeg(int l,int r,int root)
{
Pushdown(root);
if(l<=tree[root].l&&tree[root].r<=r)
{
tree[root].neg=1;//一开始忘了标记懒惰了!血崩!
tree[root].maxx*=-1;
tree[root].minn*=-1;
swap(tree[root].maxx,tree[root].minn);
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(l<=mid)DoNeg(l,r,L(root));
if(r>mid)DoNeg(l,r,R(root));
Pushup(root);
}
void GetNeg(int x,int y)
{
int t1=top[x],t2=top[y];
while(t1!=t2)
{
if(dep[t1]<dep[t2]){swap(t1,t2);swap(x,y);}
DoNeg(tid[t1],tid[x],1);//分清楚什么时候用tid映射!
x=fa[t1];t1=top[x];
}
if(x==y)return;
if(dep[x]>dep[y])swap(x,y);
DoNeg(tid[son[x]],tid[y],1);
}
void getmax(int l,int r,int root)
{
Pushdown(root);
if(l<=tree[root].l&&tree[root].r<=r){Max=max(Max,tree[root].maxx);return;}
int mid=(tree[root].l+tree[root].r)>>1;
if(l<=mid)getmax(l,r,L(root));
if(r>mid)getmax(l,r,R(root));
Pushup(root);
}
void MAX(int x,int y)
{
int t1=top[x],t2=top[y];
while(t1!=t2)
{
if(dep[t1]<dep[t2]){swap(t1,t2);swap(x,y);}
getmax(tid[t1],tid[x],1); 
x=fa[t1];t1=top[x];
}

if(dep[x]>dep[y])swap(x,y);
getmax(tid[son[x]],tid[y],1);
}
int n;
void init()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&t[i].x,&t[i].y,&t[i].v);
add(t[i].x,t[i].y);
add(t[i].y,t[i].x);
}
DFS1(1,0,1);DFS2(1,1);build(1,n,1);
for(int i=1;i<n;i++)
{
if(dep[t[i].x]>dep[t[i].y])swap(t[i].x,t[i].y);
change(tid[t[i].y],t[i].v,1);
}
}
void solve()
{
int a,b,c,d;
char cmd[10];
while(1)
{
scanf("%s",&cmd);
if(cmd[0]=='D')break;
if(cmd[0]=='Q')
{
Max=-INF;//注意还原
scanf("%d%d",&a,&b);
MAX(a,b);
printf("%d\n",Max);
}
if(cmd[0]=='C')
{
scanf("%d%d",&a,&b);
change(tid[t[a].y],b,1);
}
if(cmd[0]=='N')
{
scanf("%d%d",&a,&b);
GetNeg(a,b);
}
}
}
int main()
{
init();
solve();
return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值