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
", Query(x));
    }
    return 0;
}

考虑优化

看暴力:每次修改只会影响到它到1的链,询问也是它到1的链
那么考虑用树链剖分变成dfs序
链剖都想了那就考虑用线段树维护这个dfs序上的信息
维护dfs序上每个点子树的val和的平方和
修改?
假设修改(u)的变化量为(d)
那么该完后(u)的祖先们(i)的子树平方和变成
(sum_{jin i的子树}(sum[j]+d)^2=sum(sum^2[j]+2*d*sum[j]+d^2))
设有子树大小为(num),就是(sum(sum^2[j])+2*dsum 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
", Calc(x));
	}
    return 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/8313101.html