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

原创 2017年01月02日 21:04:46

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;
}
版权声明:如要转载,标明出处即可

POJ 2763 树链剖分

点击打开链接 题意:给一个树,然后树上的边的边权,然后两个操作,一个是询问u到v的路上权值和,一个是将第几条边的权值修改 思路:与SPOJ 375 的那道题目很像,都是边上的权值,然后维护一个线段...
  • Dan__ge
  • Dan__ge
  • 2016年07月11日 14:14
  • 1048

POJ 3237 树链剖分

点击打开链接 题意:给一个树,三种操作,一个是将第I条边的权值改变,一个是将u到v的所有边的权值取反,一个是询问u到v的路径中边的最大值 思路:和模版的树链剖分没什么区别,这题唯一的坑点就是线段树...
  • Dan__ge
  • Dan__ge
  • 2016年07月11日 16:17
  • 1014

POJ 1741 Tree【Tree,点分治】

树上的算法真的很有意思……哈哈。 给一棵边带权树,问两点之间的距离小于等于K的点对有多少个。 将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。...
  • yang_7_46
  • yang_7_46
  • 2013年08月14日 16:27
  • 12952

POJ 3237 Tree(树链剖分-线段树点更新-区间更新-区间最值查询-入边)

题意: 给出若干个操作,询问路径最大值。 思路: 裸树链剖分,只不过需要记录最大值和最小值,当翻转的时候可以标记一下,然后互换最大值和最小值。 PS: WA了5个小时,就因为线段树函数太多。。有个函数...
  • qq_33951440
  • qq_33951440
  • 2017年05月18日 15:49
  • 115

|poj 3237|树链剖分|线段树|Tree

poj 3237树剖+线段树。 刚开始想用记录该区域被NEGATE了几次,结果发现不可行,翻别人博客发现了原来维护最大值maxvmaxv和最小值minvminv,NEGATE就是maxv=−minv...
  • Darost
  • Darost
  • 2017年05月18日 21:00
  • 86

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

Description 一棵树有n个节点,n-1条边,每条边有一个权值,三种操作 CHANGE i v:将第i条边的权值改成v NEGATE a b:将a到b路径上所有边的权值取反(x->-x)...
  • V5ZSQ
  • V5ZSQ
  • 2015年12月06日 13:16
  • 339

【POJ】【P3237】【Tree】【题解】【树链剖分+线段树】

传送门:poj.org/problem?id=3237 最近刷数据结构刷上瘾了??天天刷链剖= = 、 (该学学LCT了……) Code: #include #include #include ...
  • u012732945
  • u012732945
  • 2014年05月11日 20:52
  • 498

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

题目链接:POJ 3237 Tree 题意:在树上有三种操作。 CHANGE A B 1.将第A条边的值赋值为B; NEGATE A B 2.将A节点到B节点路径上的边变为其相反数。 ...
  • u012377575
  • u012377575
  • 2015年02月03日 19:20
  • 488

【POJ3237】Tree(树链剖分+线段树(基于边权))

题目链接 题目大意: 有T组数据 给你n个点的树 有三种操作: 1.CHANGE i v :将第i条边的值改为v 2.NEGATE a b :将a点到b点路径上的边权取反 3...
  • oranges_c
  • oranges_c
  • 2017年02月04日 17:18
  • 101

POJ3237 树链剖分+线段树

# include # include # include # include # include # include # include # include # include # define...
  • u014111471
  • u014111471
  • 2014年09月29日 20:05
  • 472
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:POJ 3237 Tree (树链剖分+线段树)
举报原因:
原因补充:

(最多只允许输入30个字)