cogs1583. [POJ3237]树的维护

1583. [POJ3237]树的维护

http://www.cogs.pro/cogs/problem/problem.php?pid=1583

★★★☆   输入文件:maintaintree.in   输出文件:maintaintree.out   简单对比
时间限制:5 s   内存限制:128 MB

【题目描述】

给你由N个结点组成的树。树的节点被编号为1到N,边被编号为1到N-1。每一条边有一个权值。然后你要在树上执行一系列指令。指令可以是如下三种之一:

CHANGE i v:将第i条边的权值改成v。

NEGATE a b:将点a到点b路径上所有边的权值变成其相反数。

QUERY a b:找出点a到点b路径上各边的最大权值。

【输入格式】

输入文件的第一行有一个整数N(N<=10000)。

接下来N-1行每行有三个整数a,b,c,代表点a和点b之间有一条权值为c的边。这些边按照其编号从小到大给出。

接下来是若干条指令(不超过10^5条),都按照上面所说的格式。

输入文件的最后一行是"DONE".

【输出格式】

对每个“QUERY”指令,输出一行,即路径上各边的最大权值。

【样例输入】

3

1 2 1

2 3 2

QUERY 1 2

CHANGE 1 3

QUERY 1 2

DONE

【样例输出】

1

3

【提示】

这里的输入输出格式和POJ上原题略有不同。

【来源】

POJ 3237 Tree

难点在于取相反数操作

记录下最大值和最小值,有取相反数操作时,就把两个值变成相反数,再交换两数的值就ok,然后给这个区间打上标记(线段树的lazy标记),以后访问或更改值时记得下传标记就好。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXN = 100010;
struct Edge{
    int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[MAXN];//和p数组相反
int son[MAXN];//重儿子
int pos;
void init(){
    tot = 0;
    memset(head,-1,sizeof(head));
    pos = 0;
    memset(son,-1,sizeof(son));
}
void addedge(int u,int v){
    edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void dfs1(int u,int v,int d){
    deep[v]=d;
    fa[v]=u;
    num[v]=1;
    for(int i=head[v];i!=-1;i=edge[i].next){
        int to=edge[i].to;
        if(to==u)continue;
        dfs1(v,to,d+1);
        num[v]+=num[to];
        if(son[v]==-1||num[son[v]]<num[to])son[v]=to;
    }
}
void dfs2(int u,int v){
    top[u]=v;
    p[u]=pos++;
    if(son[u]==-1)return;
    dfs2(son[u],v);
    for(int i=head[u];i!=-1;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa[u]||to==son[u])continue;
        dfs2(to,to);
    }
}

//线段树
struct Node{
    int l,r;
    int Max;
    int Min;
    int ne;
}tr[MAXN*3];
void build(int i,int l,int r){
    tr[i].l = l;
    tr[i].r = r;
    tr[i].Max = 0;
    tr[i].Min = 0;
    tr[i].ne = 0;
    if(l == r)return;
    int mid = (l+r)/2;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}
void push_up(int i)
{
    tr[i].Max = max(tr[i<<1].Max,tr[(i<<1)|1].Max);
    tr[i].Min = min(tr[i<<1].Min,tr[(i<<1)|1].Min);
}
void push_down(int i){

    if(tr[i].l == tr[i].r)return;
    if(tr[i].ne)
    {
        tr[i<<1].Max = -tr[i<<1].Max;
        tr[i<<1].Min = -tr[i<<1].Min;
        swap(tr[i<<1].Min,tr[i<<1].Max);
        tr[(i<<1)|1].Max = -tr[(i<<1)|1].Max;
        tr[(i<<1)|1].Min = -tr[(i<<1)|1].Min;
        swap(tr[(i<<1)|1].Max,tr[(i<<1)|1].Min);
        tr[i<<1].ne ^= 1;
        tr[(i<<1)|1].ne ^= 1;
        tr[i].ne = 0;
    }
}
void update(int i,int k,int val){ // 更新线段树的第k个值为val

    if(tr[i].l == k && tr[i].r == k)
    {
        tr[i].Max = val;
        tr[i].Min = val;
        tr[i].ne = 0;
        return;
    }
    push_down(i);
    int mid = (tr[i].l + tr[i].r)/2;
    if(k <= mid)update(i<<1,k,val);
    else update((i<<1)|1,k,val);
    push_up(i);
}

void ne_update(int i,int l,int r){
    if(tr[i].l==l&&tr[i].r==r){
        tr[i].Max=-tr[i].Max;tr[i].Min=-tr[i].Min;
        swap(tr[i].Max,tr[i].Min);
        tr[i].ne^=1;return;
    }
    push_down(i);
    int mid=(tr[i].l+tr[i].r)>>1;
    if(r<=mid)ne_update(i<<1,l,r);
    else if(l>mid)ne_update((i<<1)|1,l,r);
    else ne_update(i<<1,l,mid),ne_update((i<<1)|1,mid+1,r);
    tr[i].Min=min(tr[i<<1].Min,tr[(i<<1)|1].Min);
    tr[i].Max=max(tr[i<<1].Max,tr[(i<<1)|1].Max);
}
int query(int i,int l,int r){  //查询线段树中[l,r] 的最大值 

    if(tr[i].l == l && tr[i].r == r)
        return tr[i].Max;
    push_down(i);
    int mid = (tr[i].l + tr[i].r)/2;
    if(r <= mid)return query(i<<1,l,r);
    else if(l > mid)return query((i<<1)|1,l,r);
    else return max(query(i<<1,l,mid),query((i<<1)|1,mid+1,r));
    push_up(i);
}
int findmax(int u,int v){//查询u->v边的最大值

    int f1 = top[u], f2 = top[v];
    int tmp = -100000000;
    while(f1 != f2)
    {
        if(deep[f1] < deep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        tmp = max(tmp,query(1,p[f1],p[u]));
        u = fa[f1]; f1 = top[u];
    }
    if(u == v)return tmp;
    if(deep[u] > deep[v]) swap(u,v);
    return max(tmp,query(1,p[son[u]],p[v]));
}
void Negate(int u,int v){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(deep[f1]<deep[f2])swap(f1,f2),swap(u,v);
        ne_update(1,p[f1],p[u]);
        u=fa[f1];f1=top[u];
    }
    if(u==v)return;
    if(deep[u]>deep[v])swap(u,v);
    ne_update(1,p[son[u]],p[v]);
    return;
}
int E[MAXN][3];
int main()
{
    //freopen("Cola.txt","r",stdin);
    freopen("maintaintree.in","r",stdin);
    freopen("maintaintree.out","w",stdout);
    int T,n;
    T=1;
    while(T--){
        init();
        scanf("%d",&n);
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&E[i][0],&E[i][1],&E[i][2]);
            addedge(E[i][0],E[i][1]);
            addedge(E[i][1],E[i][0]);
        }
        dfs1(0,1,0);
        dfs2(1,1);
        build(1,0,pos-1);
        for(int i=0;i<n-1;i++){
            if(deep[E[i][0]]>deep[E[i][1]])swap(E[i][0],E[i][1]);
            update(1,p[E[i][1]],E[i][2]);
        }
        char ch[10];
        int u,v;
        while(1){
            scanf("%s",ch);
            if(ch[0]==‘D‘)break;
            scanf("%d%d",&u,&v);
            if(ch[0]==‘Q‘)printf("%d\n",findmax(u,v));
            else if(ch[0]==‘C‘)update(1,p[E[u-1][1]],v);
            else Negate(u,v);
        }
    }
    return 0;
}
时间: 2024-10-30 19:04:33

cogs1583. [POJ3237]树的维护的相关文章

[POJ3237]树的维护

P1424 - [POJ3237]树的维护 Description 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1到N-1.每一条边有一个权值.然后你要在树上执行一系列指令.指令可以是如下三种之一: CHANGE i v:将第i条边的权值改成v. NEGATE a b:将点a到点b路径上所有边的权值变成其相反数. QUERY a b:找出点a到点b路径上各边的最大权值. Input 第一行有一个整数N(N<=10000). 接下来N-1行每行有三个整数a,b,c,代表点a和点b之间

COGS 1583. [POJ3237]树的维护

二次联通门 : COGS 1583. [POJ3237]树的维护 /* COGS 1583. [POJ3237]树的维护 树链剖分 + 边权化点权 线段树 单点修改 + 区间取相反数 + 查询区间最大 对于区间取相反数 考虑在线段树中维护两个值 一个区间最大, 一个区间最小 对于更改, 只需把区间最大与最小分别取相反数后交换即可 然后对于标记, 由于对区间连续取反两次相当于不变 则只需开一个bool 标记, 每次放标记时对标记取反即可 */ #include <cstdio> #define

线段树可维护的基本信息

一.区间最值 1.单点替换: 1 const int M=100001; 2 LL a[M]; 3 LL MAX[M<<2]; 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 void update(int rt){ 7 MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]); 8 } 9 void build(int l,int r,int rt){ 10 if

线段树 --- (区间维护+逆推)

Buy Tickets Time Limit:4000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue- The Lunar New Year was appro

hdu 1556 Color the ball(线段树区间维护+单点求值)

传送门:Color the ball Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 25511    Accepted Submission(s): 12393 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele

cogs 1583. [POJ 3237] 树的维护 树链剖分套线段树

1583. [POJ 3237] 树的维护 ★★★★   输入文件:maintaintree.in   输出文件:maintaintree.out   简单对比时间限制:5 s   内存限制:128 MB [题目描述] 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1到N-1.每一条边有一个权值.然后你要在树上执行一系列指令.指令可以是如下三种之一: CHANGE i v:将第i条边的权值改成v. NEGATE a b:将点a到点b路径上所有边的权值变成其相反数. QUERY a b

【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案

传送门 写在前面:虽然这是一道我再也不想写的题目,但很好很有价值 思路: cxlove大神: 要求中位数最大,首先二分中位数,然后判断可行不可行. 判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1.这样的话,求一次最大区间和 如果大于等于0,则说明可行. 这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树 具体实现 对于题目

BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)

原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面): Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒.实验在一个封闭的局域网内进行.局域网内有n台计算机,编号为1~n.一些计算机之间通过网线直接相连,形成树形的结构.局域网中有一台特殊的计算机,称之为核心计算机.根据一些初步的研究,研究员们拟定了一个一共m步的实验.实验开始之前,核

CodeForces 587 E.Duff as a Queen 线段树动态维护区间线性基

https://codeforces.com/contest/587/problem/E 一个序列, 1区间异或操作 2查询区间子集异或种类数 题解 解题思路大同小异,都是利用异或的性质进行转化,std和很多网友用的都是差分的思想,用两棵线段树 第一棵维护差分序列上的线性基,第二棵维护原序列的异或区间和,两者同时进行修改 考虑两个序列 $(a,b)(d,e)$,按照std的想法,应该是维护$(0 \^ a,a \^ b)(0 \^ d,d \^ e)$ 然后合并首尾变成$(0 \^ a,a \^