【Luogu 3676 小清新数据结构题】【动态点分治 + 思维题】

题目P3676 小清新数据结构题

实现代码能力真的太差了吧 …
不能再看别人代码了 … FLAG


(搬得 zzq 的题解 … 按照我思考这个问题的顺序换了一下顺序???

如果没有平方怎么做,即要以点 p 为根,求出每个点子树的点权和,直接加在一起。

考虑一个点 s 的点权 vs 只有它及祖先才会计入,即计入了 dis(s,p)+1 次,那么没有平方的答案就是 ni=1dis(p,i)vi+ni=1vi ,这个东西显然是可以用动态点分治维护的,参见 【ZJOI 2015 幻想乡战略游戏】【动态点分治】

现在问题就是这个平方,我们设 sx x 子树的点权和,w 为点权总和,那么我们可以发现 ni=1si(wsi) 无论根是啥都是一个定值。

那么如果我们只要在以 1 为根的时候求出 ni=1siws2i ,然后在 p 为根时求出 ni=1si ,再把以 p 为根时的 ni=1s2i 解出来即可。

p 为根时求 ni=1si 只要按照幻想乡战略游戏那么求即可。
这样以 1 为根时的 ni=1si 我们也就可以求出来了。
w 我们也直接求得了。

那么对于以 1 为根时的 ni=1siws2i 我们只需考虑如何求以 1 为根时的 ni=1s2i 即可。

这是我们注意到有一个良心的 30 部分分正好是干这个的。
我们的要求就是把一个点到根的路径上的子树和全部加上一个值,询问所有点子树平方和。

考虑对一个点 x 点权 + y 对答案的贡献,我们发现对于每个 x 的父亲 f,假设原来点权和为 p,现在就变成了 p + y,那么对答案贡献就是 (p+y)2p2=2py+y2 ,我们只要统计 x 到根的点权和即可。

那么我们只要支持将一个点到根的路径点权加上一个数 y 和统计一个点到根的点权和,直接树链剖分就可以做到两个 log

这部分实现注意:
我们维护树剖的线段树,节点赋的初值一开始就应该是以这个节点为根的子数点权和。
每次修改操作增加的值就是 y,另记一个变量统计每次贡献,贡献的计算:因为我们要算从 x 点到根的 fa[i]i=x2piy+y2 = 2yfa[i]i=xpi+fa[i]i=xy2


注意一件事情,如果一个函数中有需要先由另一个函数求得的东西,一定要注意执行顺序。
在我们不知道是否函数的执行顺序可以随意的时候,要先注意上面所说的,否则之后调程序会很麻烦,以后调程序时要先注意一下这种错误 … 日常错误 1/1

劳资调不出来了啊 … 真的肝不动了啊 … 好气啊 … 为什么死活只能拿 30 分啊 … 改天再来补坑吧 …

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const ll N = 4e5 + 5;

struct Edge {
    ll to, next, w;
}e[N << 1];

ll n, q, squ = 0, total = 0;
ll a[N], sumw[N];

ll cnt = 0;
ll head[N];
void add(ll u, ll v) {
    e[++ cnt].to = v; e[cnt].w = 1; e[cnt].next = head[u]; head[u] = cnt;
}

struct Chain {
    ll fa[N], dep[N], hson[N], size[N];
    void dfs1(ll u, ll f, ll depth) {
        fa[u] = f;
        dep[u] = depth;
        size[u] = 1;
        for (ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if (v == f) continue;
            dfs1(v, u, depth + 1);
            sumw[u] += sumw[v];
            size[u] += size[v];
            if (size[hson[u]] < size[v] || hson[u] == -1) hson[u] = v;
        }
    }

    ll tim;
    ll top[N], tid[N], Rank[N];
    void dfs2(ll u, ll tp) {
        top[u] = tp;
        tid[u] = ++ tim;
        Rank[tim] = u;
        if (hson[u] == -1) return ;
        dfs2(hson[u], tp);
        for (ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to; 
            if (v != fa[u] && v != hson[u]) dfs2(v, v);
        }
    }

    // ----------

    struct Node {
        ll sum, lazy;   
    }tree[N << 2];

    void pushup(ll i) {
        tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum;  
    }

    void build(ll rt, ll l, ll r) {
        tree[rt].lazy = 0;
        if (l == r) { tree[rt].sum = sumw[Rank[l]]; return ; }
        ll mid = (l + r) >> 1;
        build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r);
        pushup(rt);
    }

    void pushdown(ll i, ll l, ll r) {
        if (tree[i].lazy) {
            tree[i << 1].lazy += tree[i].lazy;
            tree[i << 1 | 1].lazy += tree[i].lazy;
            ll mid = (l + r) >> 1;
            tree[i << 1].sum += (mid - l + 1) * tree[i].lazy;
            tree[i << 1 | 1].sum += (r - mid) * tree[i].lazy;
            tree[i].lazy = 0;
        }
    }

    void update(ll rt, ll l, ll r, ll ql, ll qr, ll w) {
        if (ql > r || qr < l) return ;
        if (ql <= l && r <= qr) {
            tree[rt].lazy += w;
            tree[rt].sum += (r - l + 1) * w;
            return ;
        }
        pushdown(rt, l, r);
        ll mid = (l + r) >> 1;
        update(rt << 1, l, mid, ql, qr, w);
        update(rt << 1 | 1, mid + 1, r, ql, qr, w);
        pushup(rt);
    }

    ll query(ll rt, ll l, ll r, ll ql, ll qr) {
        if (ql > r || qr < l) return 0;
        if (ql <= l && r <= qr) return tree[rt].sum;
        pushdown(rt, l, r);
        ll mid = (l + r) >> 1;
        return query(rt << 1, l, mid, ql, qr) + query(rt << 1| 1, mid + 1, r, ql, qr);
    }

    // -------------

    void chainchange(ll x, ll y, ll val) {
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) swap(x, y);
            update(1, 1, n, tid[top[x]], tid[x], val);
            x = fa[top[x]];
        }
        if (dep[x] < dep[y]) swap(x, y);
        update(1, 1, n, tid[y], tid[x], val);
    }

    ll chainquery(ll x, ll y) {
        ll res = 0;
        while (top[x] != top[y]) {

            if (dep[top[x]] < dep[top[y]]) swap(x, y);
            res += query(1, 1, n, tid[top[x]], tid[x]);
            x = fa[top[x]];
        }
        if (dep[x] < dep[y]) swap(x, y);
        res += query(1, 1, n, tid[y], tid[x]);
        return res;
    }
}chain;

struct DF_tree {
    ll dffa[N], dfdep[N], dfsize[N], dfson[N], dfdis[N];
    void dfs1(ll u, ll f, ll depth) {
        dffa[u] = f;
        dfdep[u] = depth;
        dfsize[u] = 1;
        for(ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if(v != dffa[u]) {
                dfs1(v, u, depth + 1);
                dfdis[v] = dfdis[u] + e[i].w;
                dfsize[u] += dfsize[v];
                if (dfson[u] == -1 || dfsize[dfson[u]] < dfsize[v])
                    dfson[u] = v;
            }
        }
    }

    ll top[N];
    void dfs2(ll u, ll tp) {
        top[u] = tp;
        if (dfson[u] == - 1) return ;
        dfs2(dfson[u], tp);
        for(ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if(v != dffa[u] && v != dfson[u])
                dfs2(v, v);
        }
    }

    ll lca(ll a, ll b) {
        while (top[a] != top[b]) {
            if (dfdep[top[a]] > dfdep[top[b]]) a = dffa[top[a]];
            else b = dffa[top[b]];
        }
        return (dfdep[a] < dfdep[b]) ? a : b;
    }

    ll getdis(ll x, ll y) {
        return (dfdis[x] + dfdis[y]) - 2 * dfdis[lca(x, y)];
    }

    // ----------

    ll rt, sum;

    ll size[N], son[N], vis[N];
    void getrt(ll u, ll f) {
        son[u] = 1;
        size[u] = 0;
        for (ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if (v == f || vis[v]) continue;
            getrt(v, u);
            son[u] += son[v];
            size[u] = max(size[u], son[v]); 
        }
        size[u] = max(size[u], sum - son[u]);
        if (size[u] < size[rt]) rt = u;
    }

    void getsize(ll u, ll f) {
        son[u] = 1;
        for (ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if (vis[v] || v == f) continue;
            getrt(v, u);
            son[u] += son[v];
        }
    }

    ll fa[N];
    void solve(ll u, ll f) {
        vis[u] = 1; fa[u] = f;
        for (ll i = head[u]; i; i = e[i].next) {
            ll v = e[i].to;
            if (vis[v]) continue;
            getsize(v, 0);
            sum = size[0] = son[v];
            getrt(v, rt = 0);
            solve(rt, u);
        }
    }

    ll cost[N], costfa[N]; // 子树代价和 
    ll dfsum[N], dfsumfa[N]; // 子树权值和 
    void pre() {
        for (ll u = 1; u <= n; u ++) {
            dfsum[u] += a[u];
            for (ll i = u; fa[i]; i = fa[i]) {
                ll disnum = getdis(u, fa[i]);
                dfsum[fa[i]] += a[u];
                dfsumfa[i] += a[u];
                cost[fa[i]] += disnum * a[u];
                costfa[i] += disnum * a[u]; 
            }
        }
    }

    ll cal(ll u) {
        int ans = cost[u];
        for (ll i = u; fa[i]; i = fa[i]) {
            ll disnum = getdis(u, fa[i]);
            ans += cost[fa[i]] - costfa[i];
            ans += disnum * (dfsum[fa[i]] - dfsumfa[i]);
        }
        return ans + total;
    }

    void dftreechange(ll u, ll val) {
        dfsum[u] += val;
        for (ll i = u; fa[i]; i = fa[i]) {
            ll disnum = getdis(u, fa[i]);
            dfsum[fa[i]] += val;
            dfsumfa[i] += val;
            cost[fa[i]] += disnum * val;
            costfa[i] += disnum * val;
        }
    }
}dftree;

int main() {
    //freopen("1.in", "r", stdin);

    memset(chain.hson, -1, sizeof(chain.hson));
    memset(dftree.dfson, -1, sizeof(dftree.dfson));
    memset(dftree.vis, 0, sizeof(dftree.vis));
    memset(dftree.dfdis, 0, sizeof(dftree.dfdis));
    chain.tim = 0;
    scanf("%lld%lld", &n, &q);
    for (ll i = 1; i < n; i ++) {
        ll x, y;
        scanf("%lld%lld", &x, &y);
        add(x, y), add(y, x);
    }
    for (ll i = 1; i <= n; i ++) scanf("%lld", &a[i]), sumw[i] = a[i], total += a[i];

    dftree.sum = dftree.size[0] = n;
    dftree.getrt(1, dftree.rt = 0);
    ll oldrt = dftree.rt;
    dftree.solve(dftree.rt, 0);
    dftree.dfs1(oldrt, 0, 0); dftree.dfs2(oldrt, oldrt); 
    dftree.pre();
    chain.dfs1(1, 0, 0); chain.dfs2(1, 1);
    chain.build(1, 1, n);

    for (ll i = 1; i <= n; i ++) squ += sumw[i] * sumw[i];
    for (ll i = 1; i <= q; i ++) {
        ll o;
        scanf("%lld", &o);
        if (o == 1) {
            ll x, y;
            scanf("%lld%lld", &x, &y);
            ll addvar = y - a[x];
            total += addvar;
            a[x] = y;
            ll old = chain.chainquery(1, x);
            squ += ((old * addvar) << 1) + addvar * addvar * (chain.dep[x] + 1);
            chain.chainchange(1, x, addvar);
            dftree.dftreechange(x, addvar);
        }   
        else if (o == 2) {
            ll u;
            scanf("%lld", &u);
            ll lst = dftree.cal(1) * total - squ;
            ll ans = dftree.cal(u) * total - lst;
            printf("%lld\n", ans);
        }
    }
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值