POJ 3237 Tree (树链剖分+线段树)

来源:互联网 发布:搞怪照片软件 编辑:程序博客网 时间:2024/05/21 14:45

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;}
0 0
原创粉丝点击