• 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;
    }
    
    
  • 相关阅读:
    NSIS使用WinVer.nsh头文件判断操作系统版本
    批处理bat命令--获取当前盘符和当前目录和上级目录
    官方 NSIS 插件全集简单介绍
    js 的基础知识变量
    js stringObject的indexOf方法
    js 作为属性的变量
    js 不可变的原始值和可变的对象引用
    jquery完善的处理机制
    jquery对象和DOM对象的相互转换
    CSS3 animation-fill-mode 属性
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8313101.html
Copyright © 2020-2023  润新知