2282. [HZOI 2015]黑树白
★★ 输入文件:D_Tree.in
输出文件:
D_Tree.out
简单对比
时间限制:3.5 s 内存限制:512 MB
【题目描述】
给定一棵树,每个节点有点权,要求维护以下两个操作:
1、Q u v a b 本蒟蒻施展大魔法,使得树上所有点权>=a且点权<=b的点变成白点,其余的点变成黑点,并查询u到v的简单路径上有多少个白点
2、M u v 本蒟蒻施展小魔法,将u点的点权改为v
【输入格式】
第一行n,m 表示节点总数和操作次数
以下n个正整数wi表示i点的点权
以下n-1行,每行u,v描述一条边的端点
以下m行,每行一个操作如题意
注意:由于本蒟蒻的魔法太弱不稳定
所以对于每次Q和M输入的u和v,你需要令u=(u+ans)%n+1,v=(v+ans)%n+1
其中ans为上一次的答案,最开始ans=0
注意只是Q和M中输入的u和v
n,m<=80000,任意时刻点权不会超过n
【输出格式】
对于每个Q操作,输出相应答案
【样例输入】
5 5 4 3 3 1 1 2 1 3 2 4 3 5 4 M 2 1 M 4 4 M 1 4 Q 2 4 2 3 Q 4 2 1 5
【样例输出】
1 4
对于每个节点用dfs序编号,查询就变成dfs序为[l,r]之间,权值为[a,b]之间的点的个数,转换成二维平面问题就是求矩形内点的个数,不过因为是强制在线,因此不能用扫描线求解。
由于权值不会超过n,因此可以对于每种权值都建一棵线段树,然后用树状数组维护
树状数组保存权值不超过x时指向的线段树的根节点编号,线段树保存dfs序为[l,r]之间的点的个数
组合起来就是权值不超过x且dfs序在[l,r]之间的点的个数
#include<bits/stdc++.h>
using namespace std;
const int MX = 2e5 + 5;
const int MXM = 3e7 + 5;
int head[MX], a[MX];
int dfn[MX], sz[MX], son[MX], pre[MX], tp[MX], dep[MX];
int n, m, tot, rear;
int sum[MXM], ls[MXM], rs[MXM], rt[MXM], cnt;
void update(int p, int v, int l, int r, int prt,int& rt) {
rt = ++cnt;
sum[rt] = sum[prt];
ls[rt] = ls[prt]; rs[rt] = rs[prt];
sum[rt] += v;
if (l == r) return;
int m = (l + r) >> 1;
if (p <= m) update(p, v, l, m, ls[prt],ls[rt]);
else update(p, v, m + 1, r, rs[prt],rs[rt]);
}
int query(int L, int R, int l, int r, int rt) {
if (l >= L && r <= R) return sum[rt];
int m = (l + r) >> 1, ret = 0;
if (L <= m) ret += query(L, R, l, m, ls[rt]);
if (R > m) ret += query(L, R, m + 1, r, rs[rt]);
return ret;
}
struct Tree {
int n;
vector <int> T;
void init (int sz) {
T.clear();
n = sz;
T.resize(n + 1);
}
void add (int x, int p, int d) {
for (int i = x; i <= n; i += i & -i)
update(p, d, 1, n, T[i], T[i]);
}
int sum (int x, int l, int r) {
if (x > n) x = n;
int ret = 0;
for (int i = x; i > 0; i -= i & -i)
ret += query(l, r, 1, n, T[i]);
return ret;
}
int sum(int a, int b, int l, int r) {
return sum(b, l, r) - sum(a - 1, l, r);
}
} T;
struct Edge {
int v, nxt;
} E[MX * 2];
void init() {
memset(head, -1, sizeof(head));
tot = rear = cnt = 0;
}
void add_edge(int u, int v) {
E[tot].v = v;
E[tot].nxt = head[u];
head[u] = tot++;
}
void dfs(int u, int fa) {
pre[u] = fa;
sz[u] = 1; son[u] = 0;
for (int i = head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void DFS(int u, int top) {
dfn[u] = ++rear; tp[u] = top;
dep[u] = dep[pre[u]] + 1;
if (son[u]) DFS(son[u], top);
for (int i = head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if (v == pre[u] || v == son[u]) continue;
DFS(v, v);
}
}
int main() {
//freopen("in.txt", "r", stdin);
freopen("D_Tree.in", "r", stdin);
freopen("D_Tree.out", "w+", stdout);
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
add_edge(u, v); add_edge(v, u);
}
dfs(1, 0); DFS(1, 1);
T.init(n);
for (int i = 1; i <= n; i++) T.add(a[i], dfn[i], 1);
char op[2];
for (int i = 1, u, v, l, r, ans = 0; i <= m; i++) {
scanf("%s%d%d", op, &u, &v);
u = (u + ans) % n + 1; v = (v + ans) % n + 1;
if (op[0] == 'Q') {
scanf("%d%d", &l, &r);
ans = 0;
for (int f1 = tp[u], f2 = tp[v]; f1 != f2;) {
if (dep[f1] < dep[f2]) swap(f1, f2), swap(u, v);
ans += T.sum(l, r, dfn[f1], dfn[u]);
u = pre[f1]; f1 = tp[u];
}
if (dep[u] > dep[v]) swap(u, v);
ans += T.sum(l, r, dfn[u], dfn[v]);
printf("%d\n", ans);
} else {
T.add(a[u], dfn[u], -1);
T.add(a[u] = v, dfn[u], 1);
}
}
return 0;
}