2243: [SDOI2011]染色树链剖分

对于线段树的操作,维护左端值,维护右端值,维护种类数,更新的时候,如果左儿子的右端==右儿子的左端,种类数减一,剩下的就是细节了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<stdlib.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn = 222222;
int deep[maxn];
struct Node
{
    int next; int to;
}e[maxn * 2];

int len, z; int head[maxn];
int top[maxn], son[maxn], father[maxn], size[maxn], pos[maxn], val[maxn], vis[maxn];
int color[maxn << 2], sum[maxn << 2], lsum[maxn << 2], rsum[maxn << 2];

void add(int from, int to)
{
    e[len].to = to;
    e[len].next = head[from];
    head[from] = len++;
}

void init(int x)
{
    size[x] = 1; son[x] = 0;
    for (int i = head[x]; i != -1; i = e[i].next){
        int cc = e[i].to;
        if (cc == father[x]) continue;
        father[cc] = x; deep[cc] = deep[x] + 1;
        init(cc);
        size[x] += size[cc];
        if (size[son[x]] < size[cc]) son[x] = cc;
    }
}

void dfs(int x, int tp)
{
    pos[x] = ++z; vis[z] = x; top[x] = tp;
    if (son[x]) dfs(son[x], tp);
    for (int i = head[x]; i != -1; i = e[i].next){
        int cc = e[i].to;
        if (cc == son[x] || cc == father[x]) continue;
        dfs(cc, cc);
    }
}

void up(int rt)
{
    lsum[rt] = lsum[rt << 1]; rsum[rt] = rsum[rt << 1 | 1];
    if (rsum[rt << 1] == lsum[rt << 1 | 1]) sum[rt] = sum[rt << 1] + sum[rt << 1 | 1] - 1;
    else sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void build(int l, int r, int rt)
{
    color[rt] = -1;
    if (l == r){
        color[rt] = lsum[rt] = rsum[rt] = val[vis[l]];
        sum[rt] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    up(rt);
}

void down(int rt)
{
    if (~color[rt]){
        sum[rt << 1] = sum[rt << 1 | 1] = 1;
        lsum[rt << 1] = color[rt]; rsum[rt << 1] = color[rt];
        color[rt << 1] = color[rt << 1 | 1] = color[rt];
        lsum[rt << 1 | 1] = color[rt]; rsum[rt << 1 | 1] = color[rt];
        color[rt] = -1;
    }
}

void update(int L, int R, int ans, int l, int r, int rt)
{
    if (L <= l&&r <= R){
        color[rt] = ans; sum[rt] = 1;
        lsum[rt] = ans; rsum[rt] = ans; return;
    }
    int mid = (l + r) >> 1;
    down(rt);
    if (L <= mid) update(L, R, ans, lson);
    if (R > mid) update(L, R, ans, rson);
    up(rt);
}

int ask(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R) return sum[rt];
    int ans = 0;
    int mid = (l + r) >> 1;
    down(rt);
    if (L <= mid) ans += ask(L, R, lson);
    if (R > mid) ans += ask(L, R, rson);
    if (L <= mid&&R > mid&&rsum[rt << 1] == lsum[rt << 1 | 1]) ans--;
    return ans;
}

void gao(int x, int y, int ans)
{
    int fx = top[x]; int fy = top[y];
    while (fx != fy){
        if (deep[fx] < deep[fy]){
            swap(x, y); swap(fx, fy);
        }
        update(pos[fx], pos[x], ans, 1, z, 1);
        x = father[fx]; fx = top[x];
    }
    if (deep[x]>deep[y]) swap(x, y);
    update(pos[x], pos[y], ans, 1, z, 1);
}

int ask1(int key, int l, int r, int rt)
{
    if (l == r) return color[rt];
    down(rt);
    int mid = (l + r) >> 1;
    if (key <= mid) return ask1(key, lson);
    else return ask1(key, rson);
}

int gao1(int x, int y)
{
    int ans = 0;
    int fx = top[x]; int fy = top[y];
    while (fx != fy){
        if (deep[fx] < deep[fy]){
            swap(x, y); swap(fx, fy);
        }
        ans += ask(pos[fx], pos[x], 1, z, 1);
        x = father[fx];//手戳 ,写成了father[x],看了老半天
        int t = ask1(pos[x], 1, z, 1);
        int t1 = ask1(pos[fx], 1, z, 1);
        if (t == t1) ans--;
        fx = top[x];

    }
    if (deep[x]>deep[y]) swap(x, y);
    ans += ask(pos[x], pos[y], 1, z, 1);//pos[x] 顺手写成了pos[son[x]],看了好久
    return ans;
}

int main()
{
    char str[100];
    int n, m;
    int a, b, c;
    while (cin >> n >> m){
        z = len = 0;
        memset(head, -1, sizeof(head));
        for (int i = 1; i <= n; i++){
            scanf("%d", &val[i]);
        }
        for (int i = 1; i <= n - 1; i++){
            scanf("%d%d", &a, &b);
            add(a, b); add(b, a);
        }
        deep[1] = 1;
        init(1); dfs(1, 1); build(1, z, 1);
        for (int i = 0; i < m; i++){
            scanf("%s", str);
            if (str[0] == ‘C‘){
                scanf("%d%d%d", &a, &b, &c);
                gao(a, b, c);
            }
            else{
                scanf("%d%d", &a, &b);
                int t = gao1(a, b);
                printf("%d\n", t);
                //printf("%d %d %d %d jijiji\n",ask1(pos[1],1,z,1),ask1(pos[2],1,z,1),ask1(pos[3],1,z,1),ask1(pos[5],1,z,1));
            }
        }
    }
    return 0;
}
时间: 2024-10-17 03:58:36

2243: [SDOI2011]染色树链剖分的相关文章

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj-2243 2243: [SDOI2011]染色(树链剖分)

题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含

2243: [SDOI2011]染色(树链剖分+线段树)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8400  Solved: 3150[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

2243: [SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.

[bzoj 2243]: [SDOI2011]染色 [树链剖分][线段树]

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边. 下面 行每行描述一个操作: “C a

【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

luogu题解P2486[SDOI2011]染色--树链剖分+trick

题目链接 https://www.luogu.org/problemnew/show/P2486 分析 看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然... 首先我们考虑如何在序列上维护信息:从最简单的想起,如果两个相邻的元素合并,显然是这两个元素所含颜色段个数(其实就是1)加起来,如果两个元素颜色相同就减1;那么两个分别含有两个元素的相邻区间合并,还是把这两个区间所含颜色段个数加起来,如果左区间最右边的颜色等于右区间最左边的颜色就减去1. 如此我们已经得到线段树维护信息