Luogu3676: 小清新数据结构题

Posted Cyhlnj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu3676: 小清新数据结构题相关的知识,希望对你有一定的参考价值。

题面

传送门

题解

先来一发很显然的暴力

维护两个数组,一个是子树的val和,一个是子树的val和的平方和
暴力更新,暴力查询就可以获得10分吐槽一波luogu的部分分

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define Sqr(x) ((x) * (x))
using namespace std;
typedef long long ll;
const int _(2e5 + 10);

IL ll Read(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, q, fst[_], nxt[_ << 1], to[_ << 1], cnt, val[_], sum[_], f[_], fa[_], S[_], top, son[_];

IL void Add(RG int u, RG int v){  to[cnt] = v; nxt[cnt] = fst[u]; fst[u] = cnt++;  }

IL void Dfs(RG int u, RG int ff){
    fa[u] = ff; sum[u] = val[u];
    for(RG int e = fst[u]; e != -1; e = nxt[e]){
        if(to[e] == ff) continue;
        Dfs(to[e], u);
        sum[u] += sum[to[e]]; f[u] += f[to[e]];
    }
    f[u] += Sqr(sum[u]);
}

IL void Modify(RG int u, RG int d){
    top = son[u] = 0;
    for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
    for(RG int i = top; i; --i) f[S[i]] -= f[son[S[i]]] + Sqr(sum[S[i]]), sum[S[i]] -= sum[son[S[i]]];
    f[u] -= Sqr(sum[u]); sum[u] -= val[u]; val[u] = d; sum[u] += val[u]; f[u] += Sqr(sum[u]);
    for(RG int i = 1; i <= top; ++i) sum[S[i]] += sum[son[S[i]]], f[S[i]] += f[son[S[i]]] + Sqr(sum[S[i]]);
}

IL int Query(RG int u){
    S[top = 1] = u; son[u] = 0; RG int ss = 0, ff = 0;
    for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
    for(RG int i = top; i; --i){
        ss += sum[S[i]] - sum[son[S[i]]];
        ff += f[S[i]] - f[son[S[i]]] - Sqr(sum[S[i]]) + Sqr(ss);
    }
    return ff;
}

int main(RG int argc, RG char* argv[]){
    n = Read(); q = Read(); Fill(fst, -1);
    for(RG int i = 1, x, y; i < n; ++i) x = Read(), y = Read(), Add(x, y), Add(y, x);
    for(RG int i = 1; i <= n; ++i) val[i] = Read();
    Dfs(1, 0);
    while(q--){
        RG int op = Read(), x = Read(), y;
        if(op == 1) y = Read(), Modify(x, y);
        else printf("%d\n", Query(x));
    }
    return 0;
}

考虑优化

看暴力:每次修改只会影响到它到1的链,询问也是它到1的链
那么考虑用树链剖分变成dfs序
链剖都想了那就考虑用线段树维护这个dfs序上的信息
维护dfs序上每个点子树的val和的平方和
修改?
假设修改\(u\)的变化量为\(d\)
那么该完后\(u\)的祖先们\(i\)的子树平方和变成
\(\sum_{j\in i的子树}(sum[j]+d)^2=\sum(sum^2[j]+2*d*sum[j]+d^2)\)
设有子树大小为\(num\),就是\(\sum(sum^2[j])+2*d\sum sum[j] + num*d^2\)
那么我们只需要再维护每个点子树的val和的和就可以+lazy修改了
每个点子树的val和的和也很好弄直接区间加lazy就好
查询?


    S[top = 1] = u; son[u] = 0; RG int ss = 0, ff = 0;
    for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
    for(RG int i = top; i; --i){
        ss += sum[S[i]] - sum[son[S[i]]];
        ff += f[S[i]] - f[son[S[i]]] - Sqr(sum[S[i]]) + Sqr(ss);
    }
    return ff;

根据暴力推一下,\(ss,ff\)又减又加的,最后会消除一些
设根为1的答案为\(ans1\),当前询问的点到根的路径上有\(x\)个点,分别标成\(u_1到u_x,u_1就是1,u_x就是当前点\)\(sum[i]表示i子树的val和\)
那么\(ans=ans1-\sum_{i=1}^{x}sum^2[u_i]+\sum_{i=2}^{x}(sum[u_1]-sum[u_i])\)(很好推的)
化简一下
\(ans=ans1-sum^2[u_1]*(x-1)-2*sum[u_1]\sum_{i=2}^{x}sum[u_i]\)
\(sum\)和线段树+链剖查询就好了
\(sum[u_1]即sum[1]\)就是整个数的val和可以单点查询也可维护全局变量

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define Sqr(x) ((x) * (x))
using namespace std;
typedef long long ll;
const int _(2e5 + 10);

IL ll Read(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, q, fst[_], nxt[_ << 1], to[_ << 1], cnt, val[_];
int size[_], son[_], top[_], dfn[_], deep[_], fa[_], Index, id[_];
ll sums[_ << 2], sumq[_ << 2], tag[_ << 2], sum[_];

IL void Add(RG int u, RG int v){  to[cnt] = v; nxt[cnt] = fst[u]; fst[u] = cnt++;  }

IL void Dfs1(RG int u, RG int ff){
    fa[u] = ff; deep[u] = deep[ff] + 1; sum[u] = val[u]; size[u] = 1;
    for(RG int e = fst[u]; e != -1; e = nxt[e]){
        if(to[e] == ff) continue;
        Dfs1(to[e], u);
        size[u] += size[to[e]]; sum[u] += sum[to[e]];
        if(size[to[e]] > size[son[u]]) son[u] = to[e];
    }
}

IL void Dfs2(RG int u, RG int Top){
    dfn[u] = ++Index; top[u] = Top; id[Index] = u;
    if(son[u]) Dfs2(son[u], Top);
    for(RG int e = fst[u]; e != -1; e = nxt[e]) if(!dfn[to[e]]) Dfs2(to[e], to[e]);
}

IL void Build(RG int x, RG int l, RG int r){
    if(l == r){  sums[x] = sum[id[l]]; sumq[x] = Sqr(sum[id[l]]); return;  }
    RG int mid = (l + r) >> 1;
    Build(x << 1, l, mid); Build(x << 1 | 1, mid + 1, r);
    sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
}

IL void Adjust(RG int x, RG ll len, RG ll d){  tag[x] += d; sumq[x] += 2 * sums[x] * d + Sqr(d) * len; sums[x] += d * len;  }

IL void Pushdown(RG int x, RG int l, RG int mid, RG int r){  if(!tag[x]) return; Adjust(x << 1, mid - l + 1, tag[x]); Adjust(x << 1 | 1, r - mid, tag[x]); tag[x] = 0;  }

IL void Modify(RG int x, RG int l, RG int r, RG int L, RG int R, RG ll d){
    if(L <= l && R >= r){  Adjust(x, r - l + 1, d); return;  }
    RG int mid = (l + r) >> 1;
    Pushdown(x, l, mid, r);
    if(L <= mid) Modify(x << 1, l, mid, L, R, d);
    if(R > mid) Modify(x << 1 | 1, mid + 1, r, L, R, d);
    sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
}

IL ll Query(RG int x, RG int l, RG int r, RG int L, RG int R){
    if(L <= l && R >= r) return sums[x];
    RG int mid = (l + r) >> 1; RG ll ans = 0;
    Pushdown(x, l, mid, r);
    if(L <= mid) ans = Query(x << 1, l, mid, L, R);
    if(R > mid) ans += Query(x << 1 | 1, mid + 1, r, L, R);
    sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
    return ans;
}

IL void Pre_Modify(RG int u, RG ll d){
    while(top[u] ^ top[1]) Modify(1, 1, n, dfn[top[u]], dfn[u], d), u = fa[top[u]];
    Modify(1, 1, n, 1, dfn[u], d);
}

IL ll Calc(RG int u){
    RG ll num = deep[u] - deep[1], sum_1 = Query(1, 1, n, 1, 1), An1 = 0;
    while(top[u] ^ top[1]) An1 += Query(1, 1, n, dfn[top[u]], dfn[u]), u = fa[top[u]];
    if(dfn[u] > 1) An1 += Query(1, 1, n, 2, dfn[u]);
    return sumq[1] + sum_1 * (sum_1 * num - 2 * An1);
}

int main(RG int argc, RG char* argv[]){
    n = Read(); q = Read(); Fill(fst, -1);
    for(RG int i = 1, x, y; i < n; ++i) x = Read(), y = Read(), Add(x, y), Add(y, x);
    for(RG int i = 1; i <= n; ++i) val[i] = Read();
    Dfs1(1, 0); Dfs2(1, 1);
    Build(1, 1, n);
    while(q--){
        RG int op = Read(), x = Read(), y;
        if(op == 1) y = Read(), Pre_Modify(x, y - val[x]), val[x] = y;
        else printf("%lld\n", Calc(x));
    }
    return 0;
}

以上是关于Luogu3676: 小清新数据结构题的主要内容,如果未能解决你的问题,请参考以下文章

[Luogu3676]小清新数据结构题

Luogu3676 小清新数据结构题 动态点分治

[luogu3676] 小清新数据结构题 [树链剖分+线段树]

luoguP3676 小清新数据结构题 推式子(LCT维护)

lp3676 小清新数据结构题

刷题洛谷 P3676 小清新数据结构题