BZOJ 2157 旅游 树链剖分

题目大意:给出一棵树,支持以下操作:1.改变一条边的边权。2.将x到y路径的权值取反。3.查询x到y路径上最大值,最小值和权值和。

思路:好裸的链剖水题啊,唯一麻烦一点地是权值放在了边上,注意一下处理就没问题了。。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 40010
#define INF 0x3f3f3f3f
using namespace std;
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define min(a,b) ((a) < (b) ? (a):(b))
#define max(a,b) ((a) > (b) ? (a):(b))

int points,asks;
int head[MAX],total;
int _next[MAX << 1],aim[MAX << 1];

struct Edge{
    int x,y,len;
    int pos;

    Edge(int _,int __,int ___):x(_),y(__),len(___) {}
    Edge() {}
}edge[MAX];

inline void Add(int x,int y)
{
    _next[++total] = head[x];
    aim[total] = y;
    head[x] = total;
}

int father[MAX],deep[MAX],son[MAX],size[MAX];

void _DFS(int x,int last)
{
    father[x] = last;
    deep[x] = deep[last] + 1;
    size[x] = 1;
    int max_size = 0;
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last)  continue;
        _DFS(aim[i],x);
        size[x] += size[aim[i]];
        if(size[aim[i]] > max_size)
            max_size = size[aim[i]],son[x] = aim[i];
    }
}

int pos[MAX],top[MAX],cnt;

void DFS(int x,int last,int _top)
{
    pos[x] = ++cnt;
    top[x] = _top;
    if(son[x])  DFS(son[x],x,_top);
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last || aim[i] == son[x])  continue;
        DFS(aim[i],x,aim[i]);
    }
}

struct SegTree{
    int sum,_min,_max;
    bool inv;

    SegTree(int _ = 0,int __ = INF,int ___ = -INF):sum(_),_min(__),_max(___) {}
    void Inv() {
        inv ^= 1;
        sum *= -1;
        int t = _max;
        _max = -_min;
        _min = -t;
    }
}tree[MAX << 2];

inline void PushUp(int pos)
{
    tree[pos].sum = tree[LEFT].sum + tree[RIGHT].sum;
    tree[pos]._min = min(tree[LEFT]._min,tree[RIGHT]._min);
    tree[pos]._max = max(tree[LEFT]._max,tree[RIGHT]._max);
}

inline SegTree PushUp(SegTree a,SegTree b)
{
    SegTree re;
    re.sum = a.sum + b.sum;
    re._min = min(a._min,b._min);
    re._max = max(a._max,b._max);
    return re;
}

inline void PushDown(int pos)
{
    if(tree[pos].inv) {
        tree[LEFT].Inv();
        tree[RIGHT].Inv();
        tree[pos].inv = false;
    }
}

void Modify(int l,int r,int x,int pos,int c)
{
    if(l == r) {
        tree[pos].sum = tree[pos]._min = tree[pos]._max = c;
        return ;
    }
    PushDown(pos);
    int mid = (l + r) >> 1;
    if(x <= mid) Modify(l,mid,x,LEFT,c);
    else    Modify(mid + 1,r,x,RIGHT,c);
    PushUp(pos);
}

void Change(int l,int r,int x,int y,int pos)
{
    if(l == x && r == y) {
        tree[pos].Inv();
        return ;
    }
    PushDown(pos);
    int mid = (l + r) >> 1;
    if(y <= mid) Change(l,mid,x,y,LEFT);
    else if(x > mid) Change(mid + 1,r,x,y,RIGHT);
    else {
        Change(l,mid,x,mid,LEFT);
        Change(mid + 1,r,mid + 1,y,RIGHT);
    }
    PushUp(pos);
}

inline void Change(int x,int y)
{
    while(top[x] != top[y]) {
        if(deep[top[x]] < deep[top[y]])
            swap(x,y);
        Change(1,points,pos[top[x]],pos[x],1);
        x = father[top[x]];
    }
    if(x == y)  return ;
    if(deep[x] < deep[y])    swap(x,y);
    Change(1,points,pos[y] + 1,pos[x],1);
}

SegTree Ask(int l,int r,int x,int y,int pos)
{
    if(l == x && r == y)    return tree[pos];
    PushDown(pos);
    int mid = (l + r) >> 1;
    if(y <= mid) return Ask(l,mid,x,y,LEFT);
    if(x > mid)      return Ask(mid + 1,r,x,y,RIGHT);
    SegTree left = Ask(l,mid,x,mid,LEFT);
    SegTree right = Ask(mid + 1,r,mid + 1,y,RIGHT);
    return PushUp(left,right);
}

inline SegTree Ask(int x,int y)
{
    SegTree re(0,INF,-INF);
    while(top[x] != top[y]) {
        if(deep[top[x]] < deep[top[y]])
            swap(x,y);
        re = PushUp(re,Ask(1,points,pos[top[x]],pos[x],1));
        x = father[top[x]];
    }
    if(x == y)  return re;
    if(deep[x] < deep[y])    swap(x,y);
    re = PushUp(re,Ask(1,points,pos[y] + 1,pos[x],1));
    return re;
}

char c[10];

int main()
{
    cin >> points;
    for(int x,y,z,i = 1; i < points; ++i) {
        scanf("%d%d%d",&x,&y,&z);
        x++,y++;
        Add(x,y),Add(y,x);
        edge[i] = Edge(x,y,z);
    }
    _DFS(1,0);
    DFS(1,0,1);
    for(int i = 1; i < points; ++i) {
        edge[i].pos = deep[edge[i].x] > deep[edge[i].y] ? pos[edge[i].x]:pos[edge[i].y];
        Modify(1,points,edge[i].pos,1,edge[i].len);
    }
    cin >> asks;
    for(int x,y,i = 1; i <= asks; ++i) {
        scanf("%s%d%d",c,&x,&y);
        if(c[0] == 'C') Modify(1,points,edge[x].pos,1,y);
        else if(c[0] == 'N')    Change(x + 1,y + 1);
        else {
            SegTree ans = Ask(x + 1,y + 1);
            if(c[0] == 'S')         printf("%d\n",ans.sum);
            else if(c[1] == 'I')    printf("%d\n",ans._min);
            else                    printf("%d\n",ans._max);
        }
    }
    return 0;
}
时间: 2024-10-10 16:41:05

BZOJ 2157 旅游 树链剖分的相关文章

BZOJ 2157 旅行(树链剖分码农题)

写了5KB,1发AC... 题意:给出一颗树,支持5种操作. 1.修改某条边的权值.2.将u到v的经过的边的权值取负.3.求u到v的经过的边的权值总和.4.求u到v的经过的边的权值最大值.5.求u到v经过的边的权值最小值. 基于边权的树链剖分,放在线段树上变成了区间维护问题了,线段树维护4个量min,max,sum,tag就可以了. # include <cstdio> # include <cstring> # include <cstdlib> # include

BZOJ 3083 遥远的国度 树链剖分

3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连

BZOJ 2243 染色 | 树链剖分模板题进阶版

BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上还是原树上,把两个区间的信息合并的时候,要注意中间相邻两个颜色是否相同. 这代码好长啊啊啊啊 幸好一次过了不然我估计永远也De不出来 #include <cstdio> #include <cstring> #include <algorithm> using namesp

BZOJ2157: 旅游 树链剖分 线段树

http://www.lydsy.com/JudgeOnline/problem.php?id=2157 在对树中数据进行改动的时候需要很多pushdown(具体操作见代码),不然会wa,大概原因和线段树区间修改需要很多pushup是一样的. 这个轻重链的方法特别好用,虽然第一次写树链剖分但是容易理解又有优秀复杂度的结构让人情不自禁orz. (后来发现很久以前学lca的时候就学了树链剖分只不过忘了,mdzz) 因为忘了去掉测试代码的freopen,re了4次(虽然有三次就算不re也wa),发现一

bzoj 2243 染色(树链剖分)

题外话 首先这是个挺裸的题,由于太久没写剖分导致调了好久,前天调了一下午,一直查不到错 昨晚在看春晚的时候突然灵机一动,发现合并的时候出了问题,开电脑把它A掉了= = 感觉自己也蛮拼的 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1:将节点a到节点b路径上所有点都染成颜色c 2:询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1&q

BZOJ 3083 遥远的国度 树链剖分+线段树

有换根的树链剖分的裸题. 在换根的时候注意讨论. 注意数据范围要开unsigned int或longlong #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<string> #include<iomanip> #include<a

BZOJ 2819 Nim 树链剖分

题目大意:两个小人在树上玩Nim游戏,问有没有必胜策略. 思路:尼姆游戏:如果所有石子的异或值为0就是必败局面.异或有如下性质:x ^ y ^ z = x ^ (y ^ z),所以就可以进行树链剖分了.题目中还好心提醒有30%的点会使dfs爆栈..第一次写不用dfs的树链剖分,扒的别人的代码,有些丑陋. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream>

BZOJ 2819 Nim 树链剖分/DFS序+LCA+树状数组

题意:给定一棵树,每个节点是一堆石子,给定两种操作: 1.改变x号节点的石子数量 2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略 Nim游戏有必胜策略的充要条件是所有堆的石子数异或起来不为零 这题首先一看就是树链剖分 然后题目很善良地告诉我们深搜会爆栈 于是我们可以选择广搜版的树链剖分 BFS序从左到右是深搜,从右到左是回溯,一遍BFS就够 单点修改区间查询还可以套用ZKW线段树 不过这题其实不用这么麻烦 有更简单的办法 详见 http://dzy493941464.is

BZOJ 4127 Abs 树链剖分

题目大意:给定一棵树,每个点有一个整数权值(可以是负数),要求支持两种操作: 1.链上加 2.链上绝对值之和 由于加的数保证非负,因此一个负数变成一个正数最多有n次 树链剖分,在线段树中维护一下区间最大负数即可 不知道为何 写了两个线段树就TLE 把两个线段树合并成一个就7s过了 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10