spoj 2398 Qtree3

Description

给出一棵树,树节点的颜色初始时为白色,有两种操作:
0 x:把x号节点颜色取反
1 x:询问1到x路径上第一个黑点编号

Solution

最近想练练剖分和线段树,于是想到来做做Qtree系列,不会Lct嘤嘤嘤>_<
这题比较裸,直接剖分然后维护区间最浅的黑色点标号就可以了
注意到把1当做根节点,其实剖分后每个区间左区间显然是更浅的,如果有黑点直接统计答案就可以了

Code

#include <bits/stdc++.h>
using namespace std;
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int N = 100005;
int n, Q, tot, cnt, to[N << 1], nxt[N << 1], head[N], q[N], num[N], sz[N], top[N], dep[N], pre[N], son[N], id[N], w[N << 2], f[N << 2];
bool vis[N];
inline int read(int &t) {
    int ff = 1;char c;
    while (c = getchar(), c < '0' || c > '9') if (c == '-') ff = -1;
    t = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') t = t * 10 + c - '0';
    t *= ff;
}
void add(int u, int v) {
    to[tot] = v, nxt[tot] = head[u], head[u] = tot++;
    to[tot] = u, nxt[tot] = head[v], head[v] = tot++;
}
void up(int rt) {
    if (w[ls])  w[rt] = w[ls], f[rt] = f[ls];
    else w[rt] = w[rs], f[rt] = f[rs];
}
void change(int rt, int l, int r, int p) {
    if (l == r) {
        w[rt] ^= 1;
        if (w[rt])  f[rt] = num[l];
        else f[rt] = 0;
        return;
    }
    int mid = l + r >> 1;
    if (p <= mid)   change(ls, l, mid, p);
    else change(rs, mid + 1, r, p);
    up(rt);
}
int query(int rt, int l, int r, int L, int R) {
    if (!w[rt]) return 0;
    if (L <= l && R >= r)   return f[rt];
    int mid = l + r >> 1;
    if (R <= mid)   return query(ls, l, mid, L, R);
    else if (L > mid)   return query(rs, mid + 1, r, L, R);
    else {
        int t = query(ls, l, mid, L, R);
        if (t)  return t;
        return query(rs, mid + 1, r, L, R);
    }
}
int ask(int a, int b) {
    int ans = -1;
    while (top[a] != top[b]) {
        if (dep[top[a]] < dep[top[b]])  swap(a, b);
        int t = query(1, 1, n, id[top[a]], id[a]);
        if (t)  ans = t;
        a = pre[top[a]];
    }
    if (dep[a] < dep[b])    swap(a, b);
    int t = query(1, 1, n, id[b], id[a]);
    if (t)  ans = t;
    return ans;
}
void init() {
    memset(head, -1, sizeof(head));
    read(n), read(Q);
    for (int i = 1, x, y; i < n; ++i) {
        scanf("%d%d", &x, &y);
        add(x, y);
    }
    int r = 0;
    vis[dep[1] = q[0] = 1] = 1;
    for (int i = 0; i <= r; ++i)
        for (int j = head[q[i]]; ~j; j = nxt[j]) 
            if (!vis[to[j]]){
                vis[to[j]] = 1;
                dep[q[++r] = to[j]] = dep[q[i]] + 1;
                pre[q[r]] = q[i];
            }
    for (int i = r; i >= 0; --i) {
        sz[pre[q[i]]] += ++sz[q[i]];
        if (sz[son[pre[q[i]]]] < sz[q[i]])  son[pre[q[i]]] = q[i];
    }
    for (int i = 0; i <= r; ++i)
        if (!top[q[i]]) {
            for (int j = q[i]; j; j = son[j]) {
                top[j] = q[i];
                num[id[j] = ++cnt] = j;
            }
        }
}
void gao() {
    int op, x;
    for (int i = 1; i <= Q; ++i) {
        read(op), read(x);
        if (!op)    change(1, 1, n, id[x]);
        else    printf("%d\n",ask(1, x));
    }
}
int main() {
    init();
    gao();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值