qwq

CF1843F2 题解

题面

注意到边权只有 \(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;
}

作者:adam01

出处:https://www.cnblogs.com/adam01/p/18324224

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   adam01  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题