首页 > 其他分享 >「解题报告」P3703 [SDOI2017]树点涂色

「解题报告」P3703 [SDOI2017]树点涂色

时间:2023-05-18 10:55:39浏览次数:51  
标签:dep fa P3703 st 树点 int MAXN 涂色 rc

有趣题,代码超好写,而且思路超有趣!!!

首先发现操作 1 的操作都是某个点到根,不难发现这样每种颜色一定对应着树上的一条链。那么操作 2 可以直接树上查分求答案,这样我们只需要考虑维护每个点到根的链的数量了。

怎么维护链的数量?发现这个操作 1 长得和 LCT 的 Access 操作一模一样啊,所以我们直接上 LCT 维护这个东西就行了。

考虑具体怎么维护,一个点到根的链的数量等同于这个点到根经过的虚边的数量,这样我们只需要在 Access 更改虚实边的时候,对应的在线段树上区间修改一下即可。

注意到我们需要找到这条链的链顶,直接不断跳左儿子就能找到链顶。但是这个复杂度是错的,可以考虑每次跳完之后 Splay 上来保证复杂度,或者直接维护一个标记表示这个点一直跳左儿子跳到的节点即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n;
vector<int> e[MAXN];
int dep[MAXN];
int dfn[MAXN], ed[MAXN], dcnt;
struct SegmentTree {
    struct Node {
        int val, tag;
    } t[MAXN << 2];
    #define lc (i << 1)
    #define rc (i << 1 | 1)
    void tag(int i, int v) { t[i].val += v, t[i].tag += v; }
    void pushDown(int i) { tag(lc, t[i].tag), tag(rc, t[i].tag), t[i].tag = 0; }
    void add(int a, int b, int v, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) return tag(i, v);
        int mid = (l + r) >> 1;
        pushDown(i);
        if (a <= mid) add(a, b, v, lc, l, mid);
        if (b > mid) add(a, b, v, rc, mid + 1, r);
        t[i].val = max(t[lc].val, t[rc].val);
    }
    int query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) return t[i].val;
        int mid = (l + r) >> 1;
        pushDown(i);
        if (b <= mid) return query(a, b, lc, l, mid);
        if (a > mid) return query(a, b, rc, mid + 1, r);
        return max(query(a, b, lc, l, mid), query(a, b, rc, mid + 1, r));
    }
    #undef lc
    #undef rc
} st;
int fa[MAXN][22];
int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 18; i >= 0; i--) if (dep[fa[x][i]] >= dep[y])
        x = fa[x][i];
    if (x == y) return x;
    for (int i = 18; i >= 0; i--) if (fa[x][i] != fa[y][i])
        x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
void dfs(int u, int pre) {
    dep[u] = dep[pre] + 1, dfn[u] = ++dcnt;
    fa[u][0] = pre;
    for (int i = 1; i <= 18; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    st.add(dfn[u], dfn[u], dep[u]);
    for (int v : e[u]) if (v != pre) {
        dfs(v, u);
    }
    ed[u] = dcnt;
}
struct LinkCutTree {
    int t[MAXN][2], fa[MAXN], rev[MAXN], rt[MAXN];
    #define lc(x) t[x][0]
    #define rc(x) t[x][1]
    #define ident(x) (t[fa[x]][1] == x)
    #define nroot(x) (t[fa[x]][0] == x || t[fa[x]][1] == x)
    void tag(int x) { rev[x] ^= 1, swap(lc(x), rc(x)); }
    void pushDown(int x) { if (rev[x]) tag(lc(x)), tag(rc(x)), rev[x] = 0; }
    void pushUp(int x) { rt[x] = lc(x) ? rt[lc(x)] : x; }
    void rotate(int x) {
        int f = fa[x], ff = fa[f];
        int a = ident(x), b = ident(f);
        if (nroot(f)) t[ff][b] = x;
        fa[x] = ff;
        t[f][a] = t[x][!a];
        fa[t[x][!a]] = f;
        t[x][!a] = f;
        fa[f] = x;
        pushUp(f), pushUp(x);
    }
    void splay(int x) {
        stack<int> st; st.push(x);
        int y = x; while (nroot(y)) y = fa[y], st.push(y);
        while (!st.empty()) pushDown(st.top()), st.pop();
        while (nroot(x)) {
            if (nroot(fa[x])) rotate(ident(x) == ident(fa[x]) ? fa[x] : x);
            rotate(x);
        }
        pushUp(x);
    }
    void access(int x) {
        for (int y = 0; x; x = fa[y = x]) {
            splay(x);
            if (rc(x)) st.add(dfn[rt[rc(x)]], ed[rt[rc(x)]], 1);
            if (y) st.add(dfn[rt[y]], ed[rt[y]], -1);
            rc(x) = y, pushUp(x);
        }
    }
} lct;
int m;
int query(int x) {
    return st.query(dfn[x], dfn[x]);
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n; i++) {
        int u, v; scanf("%d%d", &u, &v);
        e[u].push_back(v), e[v].push_back(u);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) {
        lct.fa[i] = fa[i][0];
        lct.rt[i] = i;
    }
    while (m--) {
        int op, x; scanf("%d%d", &op, &x);
        if (op == 1) {
            lct.access(x);
        } else if (op == 2) {
            int y; scanf("%d", &y);
            printf("%d\n", query(x) + query(y) - 2 * query(lca(x, y)) + 1);
        } else if (op == 3) {
            printf("%d\n", st.query(dfn[x], ed[x]));
        }
    }
    return 0;
}

标签:dep,fa,P3703,st,树点,int,MAXN,涂色,rc
From: https://www.cnblogs.com/apjifengc/p/17411261.html

相关文章

  • 得到 K 个黑块的最少涂色次数
    给你一个长度为n 下标从0 开始的字符串 blocks ,blocks[i] 要么是 'W' 要么是 'B' ,表示第 i 块的颜色。字符 'W'和 'B' 分别表示白色和黑色。给你一个整数 k ,表示想要 连续 黑色块的数目。每一次操作中,你可以选择一个白色块将它涂成 黑色块。请你返回至......
  • 区间涂色问题
    一眼区间dp设dp[i][j]为涂完i到j所需的最小次数当a[i]==a[j]时,dp[i][j]=min(dp[i+1][j-1]+1,min(dp[i+1][j],dp[i][j-1]));为什么是dp[i+1][j-1]+1,此时会产生一个异想天开的想法,就是取遍历一遍i+1到j-1这一段字符串,看是否有a[i]字符的出现,如果出现的话dp[i][j]=dp[i+1][j......
  • P1283 平板涂色
    题目传送门一看数据,是可以爆搜的。思路我们看,当要涂一个矩形的时候,他上面的矩形就都要涂掉于是我们就可以自然而然的想到拓扑或者说我们把整个平板抽象成一个有向图,每个矩形就是一个点,他的限制就是边比如样例:就可以抽象成建图就可以暴力建,才100*100的数据然后就在图上......
  • 2379. 得到 K 个黑块的最少涂色次数
    题目链接:2379.得到K个黑块的最少涂色次数方法一:前缀和解题思路通过前缀和计算任意子区间\([i,i+k-1]\)中字母\(W\)的数量,\(ans=min([i,i+k-1].count('W'),i=0,1,...)。\)代码classSolution{public:intminimumRecolors(stringblocks,intk......
  • 力扣简2379 得到第k个黑块的最少涂色次数
    20230310每日一题滑动窗口题 classSolution{publicintminimumRecolors(Stringblocks,intk){intres=Integer.MAX_VALUE,len=blocks.length();......
  • 2379. 得到 K 个黑块的最少涂色次数
    给你一个长度为n 下标从0 开始的字符串 blocks ,blocks[i] 要么是 'W' 要么是 'B' ,表示第 i 块的颜色。字符 'W'和 'B' 分别表示白色和黑色。给你一个整......
  • 力扣---2379. 得到 K 个黑块的最少涂色次数
    给你一个长度为n下标从0开始的字符串blocks,blocks[i]要么是'W'要么是'B',表示第i块的颜色。字符'W'和'B'分别表示白色和黑色。给你一个整数k,表示想要连......
  • P4170 [CQOI2007]涂色(思维好题)
    P4170[CQOI2007]涂色-洛谷|计算机科学教育新生态(luogu.com.cn)设DP[i][j]为完成(i-j)区间的最少涂鸦次数。考虑dp[i][j]的转移:重点:如果s[i]==s[j],dp[i][j]=min(dp[......
  • 涂满它!(涂色问题 (原题:水叮当的舞步))题解
    F.涂满它!内存限制:256MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述Flood-it是谷歌+平台上的非常好玩的一款游戏,游戏界面如下所示:在......
  • CQOI2007,洛谷P4710涂色
    题目描述假设你有一条长度为\(5\)的木版,初始时没有涂过任何颜色。你希望把它的\(5\)个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为\(5\)的字符串表示这个目......