CF1843F2 題解

adam01發表於2024-07-25

題面

注意到邊權只有 \(1,-1\),所以有結論:存在值為 \(v\) 的子段當且僅當 \(v\in[\) 最小子段和,最大子段和 \(]\)

證明:因為移動區間端點,區間和變化連續(+1/-1),從最小子段移動到最大子段,子段和一定經過 \(v\),所以得證。

於是只要樹剖維護最小最大子段和即可。

和線段樹上維護的資料一樣,查詢時維護兩側的四個資料(Lmax,Rmax,max,sum),轉移時和從線段樹上查詢出的四個資料合併。


注意兩側的轉移是相反的(一個向上一個向下,在加號不同側)。

求最小子段和等於求 原序列全部取負數 的 最大子段和 的 相反數。


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

struct node
{
    int l, m, r, s;
    node operator+(node x)
    {
        return {max(l, s + x.l), max({m, x.m, r + x.l}), max(x.r, x.s + r), s + x.s};
    }
    node rev() {swap(l, r); return *this;}
};

const int N = 2e5 + 5;

int b[N], c[N];
vector<int> e[N];
int dfn[N], son[N], sz[N], dep[N], top[N], fa[N], id[N], ts;

struct sgt
{
    node a[N << 2];
    void pushup(int x)
    {
        a[x] = a[x << 1] + a[x << 1 | 1];
    }
    void build(int x, int l, int r, int b[])
    {
        if(l == r)
        {
            a[x] = {max(0, b[id[l]]), max(0, b[id[l]]), max(0, b[id[l]]), b[id[l]]};
            return;
        }
        int mid = l + r >> 1;
        build(x << 1, l, mid, b);
        build(x << 1 | 1, mid + 1, r, b);
        pushup(x);
    }
    node qry(int ql, int qr, int l, int r, int x)
    {
        if(ql <= l && r <= qr) return a[x];
        int mid = l + r >> 1;
        if(mid < ql) return qry(ql, qr, mid + 1, r, x << 1 | 1);
        if(mid >= qr) return qry(ql, qr, l, mid, x << 1);
        return qry(ql, qr, l, mid, x << 1) + qry(ql, qr, mid + 1, r, x << 1 | 1);
    }
}t, p;

void dfs1(int x, int fa)
{
    son[x] = 0;
    ::fa[x] = fa;
    dep[x] = dep[fa] + 1;
    sz[x] = 1;
    for(int i : e[x])
    {
        if(i == fa) continue;
        dfs1(i, x);
        sz[x] += sz[i];
        if(sz[i] > sz[son[x]]) son[x] = i;
    }
}

void dfs2(int x, int tp)
{
    top[x] = tp;
    dfn[x] = ++ts;
    id[ts] = x;
    if(son[x]) dfs2(son[x], tp);
    for(int i : e[x])
        if(i != fa[x] && i != son[x])
            dfs2(i, i);
}

int n;

int qry(int x, int y, sgt &t)
{
    node ans[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};
    int f = 0;
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x, y), f ^= 1;
        if(f == 0) ans[f] = ans[f] + t.qry(dfn[top[x]], dfn[x], 1, n, 1).rev();
        else ans[f] = t.qry(dfn[top[x]], dfn[x], 1, n, 1) + ans[f];
        x = fa[top[x]];
    }
    if(dep[x] < dep[y]) swap(x, y), f ^= 1;
    if(f == 0) ans[f] = ans[f] + t.qry(dfn[y], dfn[x], 1, n, 1).rev();
    else ans[f] = t.qry(dfn[y], dfn[x], 1, n, 1) + ans[f];
    return (ans[0] + ans[1]).m;
}

struct QU{int x, y, k;}qu[N];
int cntq;

void solve()
{
    int q;cin >> q;
    n = 1;cntq = 0;ts = 0;
    b[1] = 1;c[1] = -1;
    e[1].clear();
    for(int i = 1; i <= q; i ++)
    {
        char op;
        int x, y, k;
        cin >> op >> x >> y;
        if(op == '+')
        {
            n ++;
            e[n].clear();
            e[x].push_back(n);
            e[n].push_back(x);
            b[n] = y;
            c[n] = -y;
        }
        else
        {
            cin >> k;
            qu[++cntq] = {x, y, k};
        }
    }
    dfs1(1, 0);
    dfs2(1, 1);
    t.build(1, 1, n, b);
    p.build(1, 1, n, c);
    for(int i = 1; i <= cntq; i ++)
    {
        if(qry(qu[i].x, qu[i].y, t) >= qu[i].k && -qry(qu[i].x, qu[i].y, p) <= qu[i].k) cout << "YES\n";
        else cout << "NO\n";
    }
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    int t;cin >> t;while(t --) solve();

    return 0;
}