染色「SDOI 2011」

【题目描述】
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

请你写一个程序依次完成这m个操作。

【输入格式】
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面n-1行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面m行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

【输出格式】
对于每个询问操作,输出一行答案。

题解
看到这种树上操作路径的题大概率就是树链剖分了
先考虑一下 如果是一条链要怎么做
显然 可以用线段树维护 每个区间记录三个值:此区间内的颜色段数量,区间最左边点的颜色,区间最右边点的颜色
那么 一个区间的颜色段数量=左区间颜色段数量+右区间颜色段数量-(左区间最右颜色==右区间最左颜色);最左边点颜色=左区间最左点颜色 最右边点颜色=右区间最右边点颜色
查询也是差不多 把左儿子返回的结果(这里可以返回一个结构体)和右儿子返回的结果按上一行的方法合并一下即可

然而树剖查询一条路径时是有最多log次的线段树区间查询 而不是直接查询一个区间 怎么合并这些查询答案统计出最终答案呢
其实只需要记录一下上一次查询区间的左端点颜色 如果和这一次查询区间的右端点颜色相同 答案-1即可
具体可见代码 统计答案这段还是有点抽搐

ps: 这题线段树区间修改要打懒标记

【代码】

#include <bits/stdc++.h>
using namespace std;

int n, m, a[100005], b1, b2, b3, lst[10];
int head[100005], pre[200005], to[200005], sz;
int fa[100005], d[100005], st[100005], dfn[100005], son[100005], pos[100005], siz[100005], tme;
char tp[5];
struct segtree{
    int l, r, lcol, rcol, cnt, tag;
    segtree() {
        lcol = rcol = tag = -1; l = r = cnt = 0;
    }
} tr[400005];

inline void addedge(int u, int v) {
    pre[++sz] = head[u]; head[u] = sz; to[sz] = v;
}

namespace Segtree{
    inline void pushup(int ind) {
        tr[ind].cnt = tr[ind<<1].cnt + tr[ind<<1|1].cnt - (tr[ind<<1].rcol == tr[ind<<1|1].lcol);
        tr[ind].lcol = tr[ind<<1].lcol; tr[ind].rcol = tr[ind<<1|1].rcol;
    }

    inline void pushdown(int ind) {
        if (tr[ind].tag == -1) return;
        tr[ind<<1].cnt = tr[ind<<1|1].cnt = 1;
        tr[ind<<1].lcol = tr[ind<<1|1].lcol = tr[ind<<1].rcol = tr[ind<<1|1].rcol = tr[ind].tag;
        tr[ind<<1].tag = tr[ind<<1|1].tag = tr[ind].tag; tr[ind].tag = -1;
    }

    void build(int ind, int l, int r) {
        tr[ind] = segtree();
        tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
        if (l == r) {
            tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = a[pos[l]];
            return;
        }
        int mid = (l + r) >> 1;
        build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
        pushup(ind);
    }

    void update(int ind, int x, int y, int v) {
        int l = tr[ind].l, r = tr[ind].r;
        if (x <= l && r <= y) {
            tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = v; tr[ind].tag = v; return;
        }
        int mid = (l + r) >> 1;
        pushdown(ind);
        if (x <= mid) update(ind<<1, x, y, v);
        if (mid < y) update(ind<<1|1, x, y, v);
        pushup(ind);
    }

    inline segtree merge(segtree a, segtree b) {
        segtree ret = segtree();
        ret.cnt = a.cnt + b.cnt - (a.rcol == b.lcol);
        ret.lcol = a.lcol; ret.rcol = b.rcol;
        return ret;
    }

    segtree query(int ind, int x, int y) {
        int l = tr[ind].l, r = tr[ind].r;
        if (x <= l && r <= y) {
            return tr[ind];
        }
        int mid = (l + r) >> 1;
        pushdown(ind);
        segtree ret1 = segtree(), ret2 = segtree();
        if (x <= mid) ret1 = query(ind<<1, x, y);
        if (mid < y) ret2 = query(ind<<1|1, x, y);
        if (!ret1.cnt) return ret2;
        else if (!ret2.cnt) return ret1;
        else return merge(ret1, ret2);
    }
}

using namespace Segtree;

namespace treechains{
    void dfs1(int x, int f) {
        siz[x] = 1;
        for (int i = head[x]; i; i = pre[i]) {
            int y = to[i];
            if (y == f) continue;
            d[y] = d[x] + 1; fa[y] = x;
            dfs1(y, x); siz[x] += siz[y];
            if (siz[y] > siz[son[x]]) son[x] = y;
        }
    }

    void dfs2(int x, int start) {
        dfn[x] = ++tme; pos[tme] = x; st[x] = start;
        if (son[x]) dfs2(son[x], start);
        for (int i = head[x]; i; i = pre[i]) {
            int y = to[i];
            if (y != fa[x] && y != son[x]) dfs2(y, y);
        }
    }

    void change(int x, int y, int z) {
        while (st[x] != st[y]) {
            if (d[st[x]] < d[st[y]]) swap(x, y);
            update(1, dfn[st[x]], dfn[x], z);
            x = fa[st[x]];
        }
        if (dfn[x] > dfn[y]) swap(x, y);
        update(1, dfn[x], dfn[y], z);
    }

    int ask(int x, int y) {
        int ret = 0, o = 0; lst[0] = lst[1] = -1;
                //lst[0]: x~lca这条链上上一次查询的左端点颜色; lst[1]: y~lca这条链上上一次查询的左端点颜色
        segtree tmp = segtree();
        while (st[x] != st[y]) {
            if (d[st[x]] < d[st[y]]) swap(x, y), o ^= 1;
            tmp = query(1, dfn[st[x]], dfn[x]);
            ret += tmp.cnt - (tmp.rcol == lst[o]); lst[o] = tmp.lcol;
            x = fa[st[x]];
        }
        if (dfn[x] > dfn[y]) swap(x, y), o ^= 1;
        tmp = query(1, dfn[x], dfn[y]);
        if (!o) {
            ret += tmp.cnt - (lst[0] == tmp.lcol) - (lst[1] == tmp.rcol);
        } else {
            ret += tmp.cnt - (lst[1] == tmp.lcol) - (lst[0] == tmp.rcol);
        }
        return ret;
    }
}

using namespace treechains;

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i < n; i++) {
        int x, y; scanf("%d %d", &x, &y);
        addedge(x, y); addedge(y, x);
    }
    dfs1(1, 0); dfs2(1, 1);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        scanf("%s", tp);
        if (tp[0] == 'C') {
            scanf("%d %d %d", &b1, &b2, &b3);
            change(b1, b2, b3);
        } else {
            scanf("%d %d", &b1, &b2);
            printf("%d\n", ask(b1, b2));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM26.html

时间: 2024-08-06 16:48:48

染色「SDOI 2011」的相关文章

保护出题人「SDOI 2013」

题意 有n个关卡,每一关都会在僵尸队列的排头添加一只僵尸,同时排头与家的距离会发生变化.相邻僵尸之间的距离固定为\(d\). 对于每一关,可以放置一颗任意攻击力的植物(每颗植物仅作用于当前关卡).求攻击力总和最小值. 思路 显然\(f[i]=max(\frac{sum[i]-sum[j-1]}{x[i]+d*(i-j)})\),由于斜率没有单调性,所以二分凸包. 另注:题目的取整指的是四舍五入而不是向下取整,恶臭. 代码 #include <bits/stdc++.h> using names

树的旋转「POI 2011」

[题目描述] 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1\dots n\)的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少. [输入格式] 第一行n下面每行,一个数x 如果\(x=0\),表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息, 如果\(x\ne 0\),表示这个节点是叶子节点,权值为\(x\) [输出格式] 一行,

OpenSSL 爆新漏洞「中间人攻击」

今年 4 月 8 日,OpenSSL 曾曝出严重的安全漏洞「心脏出血」.它使攻击者能够从内存中读取多达 64 KB 的数据,从而可以实时获取到以 https 开头网址的用户的重要信息,包括登录账号和密码等. 如今「心脏出血」还未被完全修复,又出现了新的漏洞.本周 OpenSSL 紧急通知用户,发现一个名为「中间人攻击」(man-in-the-middle attack) 的漏洞,黑客可以利用这一漏洞截获并读取用户隐私信息. OpenSSL 心脏出血 中间人攻击 安全漏洞 OpenSSL漏洞 「中

Netflix 不想交「苹果税」,新用户不能在 iOS 端付费了

新上线的电影<黑镜>的开放式结局让 Netflix 又一次刷新了观众的观影体验,而这家已经成为流媒体行业标杆的公司也有了更多的底气拒绝「苹果税」,他们宣布今后新用户将无法从 iOS 渠道付费,建议从网页端订阅服务. 「我们不再支持 iTunes 作为新用户的订阅方式,」一位 Netflix 的发言人向 VentureBeat 证实,不过他补充说老用户仍可在 iOS 端进行应用内购买. Netflix 没有透露这一变化具体是何时上线的,但平台客服表示大概是从上个月底开始的,此外,客服人员还确认老

「不会」等价类计数

完全忘了TnT 然而这种类型的题目好像没考过.. 先复习一下万能的burnside引理, 啊不先复习一下定义(有些是本蒻自己yy的可能并不准确) 一个物体:被染色的对象 一个元素:一种染色方案 一个置换\(g\):一种让物体交换位置的变换方法 一个置换群\(G\):里面的置换满足封闭性结合律单位元逆元 一个循环(对于一个置换i来说的):不停地用一个置换作用于所有物体,物体呈现循环运动的轨迹(数目记为\(h_i\)) 一个不动点(对于一个置换i来说的):被该置换作用后,不发生改变的一个元素(数目记

怎样将「插件化」接入到项目之中?

本期移动开发精英社群讨论的主题是「插件化」,上网查了一下,发现一篇 CSDN 博主写的文章<Android 使用动态载入框架DL进行插件化开发>.此处引用原作者的话: 随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余添加.项目新功能的加入,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,仅仅能紧急公布补丁版本号,强制用户进行更新.结果频繁的更新.反而easy减少用户使用黏性,或者是公司业务的不断发展,同系的应用越来越多,传统方式须要通过用户量最大的主项目进行引导下载并安装.

「C语言」常量和变量的表示及应用

先发布,还在修改完善中.. 在程序运行中,其值不能改变的量成为常量.在基本数据类型中,常量可分为整型常量.实型常量.符号常量和字符型常量(包括字符常量和字符串常量),现分别介绍如下: 整型常量 即整常数,由一个或多个数字组成,可以带正负号 C语言中整型常量可用十进制.八进制和十六进制3种形式表示 十进制整数:由0~9数字组成,不能以0开始,没有前缀 八进制整数:以0为前缀,其后由0~7的数字组成,没有小数部分 十六进制整数:以0x或0X开头,其后由0~9的数字和a~f(或A~F字母组成) 另外长

Chrome 扩展 Stylish :给不喜欢某个网站一键「换肤」

原文地址:http://whosmall.com/?post=419 本文标签: Chrome扩展 Chrome浏览器 Chrome插件 Chrome扩展Stylish Stylish是什么 Stylish 是什么? 开门见山,Stylish 的作用是,它可以把百度首页变成这样: 它还能把知乎「拍扁」,让微博网页版变得简洁无比,让 Feedly 用上Material Design-- 这个神奇的 Stylish实际上是一个浏览器插件,适用于 Chrome,Firefox,Opera 以及 Saf

3D高科技投影 麦可「复活登台」幕后

美国告示牌音乐颁奖典礼,日前在赌城盛大举行,主办单位利用高科技投影技术,让麦可杰克森「复活」登台表演,3D全像投影,加上影片与真人舞群无缝接轨,高科技让过世的大明星彷佛活了过来. 流行乐天王麦可杰克森死而复生,过世将近5年的他,又现身在今年美国告示牌音乐颁奖典礼上,金光闪闪现身舞台中央,麦可杰克森回来了,再现招牌的动感舞步,流露巨星风采,主办单位利用3D全像摄影技术,秘密制作了半年多,把他带回到世人眼前. 特效专家:「观众在告示牌典礼上看到的是,麦可的头部数字影像,连接到一名演员身上,我们实时捕