Codeforces Round 881 (Div. 3) F2. Omsk Metro (hard version) (线段树 )
大致题意:
动态给定一颗树,树上每个结点的权值一定为1或者-1。最开始有1号点,权值为1。
输入n表示有n个操作,当第一个符号为+ a b表示添加结点操作,编号依次递增。a表示为当前编号的父节点,b表示当前编号的权值。
输入? a b c表示查询a到b的路径上有没有子区间和为c,如果有输出YES,没有输出NO。
解题思路:
因为每个点上的权值不是1就是-1,也就是说我们只需要找出这个路径上的最大连续字段和和最小连续字段和,只要是在这个区间内都能满足答案,所以采用树剖来解决区间问题,然后树剖一定要注意维护区间的顺序,最后注意需要特判为0的时候。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e5 + 10;
const int M = 4e5 + 10;
const int INF = 0x3f3f3f3f;
typedef std::pair<int, int> PII;
int n, m;
int val[N];
int w[N], h[N], e[M], ne[M], idx;
int id[N], nw[N], cnt;
int dep[N], sz[N], top[N], fa[N], son[N], ans[N];
inline void add(int a, int b) {
ne[idx] = h[a], e[idx] = b, h[a] = idx ++;
}
struct node{
int l, r;
int lmx, rmx, mx, sum;
int lmn, rmn, mn;
}tr[N << 2];
inline void pushup(node &u,node &l,node &r){
u.sum = l.sum + r.sum;
u.lmx = std::max(l.lmx, l.sum + r.lmx);
u.rmx = std::max(r.rmx, r.sum + l.rmx);
u.lmn = std::min(l.lmn, l.sum + r.lmn);
u.rmn = std::min(r.rmn, r.sum + l.rmn);
u.mx = std::max({l.mx, r.mx, l.rmx + r.lmx});
u.mn = std::min({l.mn, r.mn, l.rmn + r.lmn});
}
inline void pushup(int u){
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
inline void build_tree(int u, int l, int r){
tr[u] = {l, r};
if(l == r){
tr[u].lmx = tr[u].rmx = tr[u].mx = tr[u].sum =
tr[u].lmn = tr[u].rmn = tr[u].mn = val[nw[l]];
return ;
}
int mid = l + r >> 1;
build_tree(u << 1, l, mid);
build_tree(u << 1 | 1, mid + 1, r);
pushup(u);
}
inline node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
if(r <= mid) return query(u << 1, l, r);
if(l > mid) return query(u << 1 | 1, l ,r);
node res;
node ls = query(u << 1, l, r);
node rs = query(u << 1 | 1, l, r);
pushup(res, ls, rs);
return res;
}
inline int LCA(int u, int v){
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
if(u == fa[top[u]]) return u;
u = fa[top[u]];
}
if (dep[u] < dep[v]) std::swap(u, v);
return v;
}
//统计子树大小 并且 找出重儿子
inline void dfs1(int u, int father, int depth){
dep[u] = depth, fa[u] = father, sz[u] = 1;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == father) continue;
dfs1(j, u, depth + 1);
sz[u] += sz[j];
if (sz[son[u]] < sz[j]) son[u] = j;
}
}
//dfs序 nw标记dfs为cnt的时候对应的树上点的权值 top标记父亲是谁
inline void dfs2(int u, int t){
id[u] = ++ cnt, nw[cnt] = u, top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
inline node query_path(int u,int v){
node left = {0, 0, -INF, -INF, -INF, 0, INF, INF, INF};
node right = left;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) std::swap(left, right), std::swap(u, v);
node res = query(1, id[top[u]], id[u]);
node s = left;
pushup(left, res, s);
u = fa[top[u]];
}
if(dep[u] < dep[v]) std::swap(u, v), std::swap(left, right);
node res = query(1, id[v], id[u]);
node s = left;
pushup(left, res, s);
std::swap(left.lmx, left.rmx);
std::swap(left.lmn, left.rmn);
node ans;
pushup(ans, left, right);
return ans;
}
inline void Init(){
dfs1(1, -1, 1);
dfs2(1, 1);
}
struct Message {
int a, b, c, d;
}msg[N];
inline void solve() {
std::cin >> n;
idx = cnt = 0;
for (int i = 1; i <= n + 1; i ++) h[i] = -1, son[i] = 0;
int now = 1;
val[1] = 1;
for (int i = 1; i <= n; i ++) {
char ch;
int a, b, c;
std::cin >> ch >> a >> b;
if (ch == '?') {
std::cin >> c;
msg[i] = {0, a, b, c};
}
else {
++ now;
add(now, a);
add(a, now);
msg[i] = {1, a, b};
val[now] = b;
}
}
Init();
build_tree(1, 1, now);
for (int i = 1; i <= n; i ++) {
if (!msg[i].a) {
int a = msg[i].b, b = msg[i].c, c = msg[i].d;
if (a == b) {
if (c == 0 || val[a] == c) std::cout << "YES" << '\n';
else std::cout << "NO" << '\n';
continue;
}
node cur = query_path(a, b);
cur.mn = std::min(0, cur.mn);
cur.mx = std::max(0, cur.mx);
if (c >= cur.mn && c <= cur.mx) {
std::cout << "YES" << '\n';
} else {
std::cout << "NO" << '\n';
}
}
}
}
int main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --) solve();
return 0;
}