zoukankan      html  css  js  c++  java
  • P3676 小清新数据结构题

    P3676 小清新数据结构题

    [sum_{u=1}^{n}left(sum_{vin subtree(u)}A_v ight)^2\ ]

    后面那个东西就是子树内两两权值乘积之和。

    直接维护平方必然不行,都是维护一次然后搞一些操作间接维护的。

    构造一个函数

    [S=sum A_i\ C(rt)=sum_{u=1}^{n}A_uoperatorname{dis}(rt,u)\ sum_{u=1}^{n}sum_{vin subtree(u)}A_v = sum_{u=1}^{n}A_u(operatorname{dis}(rt,u)+1)=C(rt)+S\ ]

    这个是可以维护的,修改查询都可以 (O(log n))

    (S*(C(rt)+S)) 会计算到每一颗子树内每一个结点和所有结点权值乘积。

    多算的是跨过每一条边的两颗子树内两两结点点权乘积,设为 (D)

    可以发现修改一个点的点权对于 (D) 的贡献就是 (Delta imes C(x)) ,初始值可以直接 (O(n)) 算。

    [ANS=S(C(rt)+S)-D ]

    我是真的太逊了啊,不知道为啥你们都能理解 (C(x)) 的计算。

    维护:

    [R_1(u)=sum_{vin subtree(u)}A_ioperatorname{dis}(u,v)\ R_2(u)=sum_{vin subtree(u)}A_ioperatorname{dis}(vt_u,v)\ R_3(u)=sum_{vin subtree(u)}A_i\ ext{*注:vt是分治树上的父亲,subtree全针对分治树} ]

    那么

    [C(rt)=R_1(rt)+sum_{uin vt_{rt}} R_1(vt_u)-R_2(u)+(R_3(vt_u)-R_3(u)) imes operatorname{dis}(vt_u,rt) ]

    实现的时候判断 (vt_u) 是否存在即可,只有 (vt_u) 存在才加后面那个贡献。

    诶诶诶?我 悟 了!确实很好理解啊,怎么之前一直觉得很难理解呢???

    我过了一年终于理解了/ll。这里放一张自己画的图,应该很有帮助(以这题为例):

    可以看到图中分成了两部分画,所有 (vt) 是黑色中轴上的点,(rt) 是最下面那个子树的根(三角形代表子树)。

    第一部分:(R_1(rt)) ,就是它子树内的点对它的贡献。

    第二部分 (sum R_1(vt_u)-R_2(u)+(R_3(vt_u)-R_3(u)) imes operatorname{dis}(vt_u,rt)) ,是子树外的部分。我们每次让绿色部分的点走到紫色结点,然后从紫色结点往下走到 (rt) 就能完成统计答案的任务了。

    绿色部分走到紫色结点的贡献就是紫色结点子树内对它的贡献,减去红色部分对它的贡献。

    悟了之后,写完过了编译就AC了,真舒服。

    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define mkp(x,y) make_pair(x,y)
    #define pb(x) push_back(x)
    #define sz(v) (int)v.size()
    typedef long long LL;
    typedef double db;
    template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
    template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
    #define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
    #define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    
    const int N=200005;
    int n,q,a[N],rt;
    LL S,D,R1[N],R2[N],R3[N],sum[N];
    int fa[N],top[N],son[N],dep[N],siz[N];
    int hed[N],et;
    struct edge{int nx,to;}e[N<<1];
    void adde(int u,int v){e[++et].nx=hed[u],e[et].to=v,hed[u]=et;}
    
    namespace Tree{
    void dfs1(int u,int ft){
    	siz[u]=1,dep[u]=dep[ft]+1,sum[u]=a[u];
    	for(int i=hed[u];i;i=e[i].nx){
    		int v=e[i].to;if(v==ft)continue;
    		fa[v]=u,dfs1(v,u),siz[u]+=siz[v],sum[u]+=sum[v];
    		if(siz[v]>siz[son[u]])son[u]=v;
    	}
    	D+=sum[u]*(S-sum[u]);
    }
    void dfs2(int u,int tp){
    	top[u]=tp;
    	if(son[u])dfs2(son[u],tp);
    	for(int i=hed[u];i;i=e[i].nx){
    		int v=e[i].to;
    		if(v!=fa[u]&&v!=son[u])dfs2(v,v);
    	}
    }
    int LCA(int x,int y){
    	while(top[x]!=top[y])dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
    	return dep[x]<dep[y]?x:y;
    }
    int dis(int x,int y){return dep[x]+dep[y]-(dep[LCA(x,y)]<<1);}
    }
    using Tree::LCA;
    using Tree::dis;
    
    namespace DFZ{
    int siz[N],mx[N],tsiz,vt[N];
    bool used[N];
    void getrt(int u,int ft){
    	siz[u]=1,mx[u]=0;
    	for(int i=hed[u];i;i=e[i].nx){
    		int v=e[i].to;if(v==ft||used[v])continue;
    		getrt(v,u),siz[u]+=siz[v];
    		ckmax(mx[u],siz[v]);
    	}
    	ckmax(mx[u],tsiz-siz[u]);
    	if(mx[u]<mx[rt])rt=u;
    }
    bool qwq[N];
    void solve(int x){
    	used[x]=1;
    	for(int i=x;i;i=vt[i]){
    		R1[i]+=a[x]*dis(i,x),R3[i]+=a[x];
    		if(vt[i])R2[i]+=a[x]*dis(vt[i],x);
    	}
    	for(int i=hed[x];i;i=e[i].nx){
    		int y=e[i].to;if(used[y])continue;
    		tsiz=siz[y],rt=0,getrt(y,x),vt[rt]=x,solve(rt);
    	}
    }
    LL calc(int x){
    	LL res=R1[x];
    	for(int i=x;vt[i];i=vt[i])
    		res+=R1[vt[i]]-R2[i]+(R3[vt[i]]-R3[i])*dis(vt[i],x);
    	return res;
    }
    void upd(int x,int y){
    	for(int i=x;i;i=vt[i]){
    		R1[i]+=y*dis(i,x),R3[i]+=y;
    		if(vt[i])R2[i]+=y*dis(vt[i],x);
    	}
    }
    
    void change(int x,int y){
    	D+=calc(x)*(y-a[x]),S+=y-a[x],upd(x,y-a[x]),a[x]=y;
    }
    LL query(int x){return S*(calc(x)+S)-D;}
    void build(){mx[rt=0]=tsiz=n,getrt(1,0),solve(rt);}
    }
    signed main(){
    	n=read(),q=read();
    	rep(i,2,n){
    		int x=read(),y=read();
    		adde(x,y),adde(y,x);
    	}
    	rep(i,1,n)a[i]=read(),S+=a[i];
    	Tree::dfs1(1,0),Tree::dfs2(1,1);
    	DFZ::build();
    	while(q--){
    		int op=read(),x=read();
    		if(op==1)DFZ::change(x,read());
    		else printf("%lld
    ",DFZ::query(x));
    	}
    	return 0;
    }
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    AWVS——windows下扫描(上)
    中介者模式
    设计模式-类型2
    设计模式=类型
    C++ 1
    字符占字节
    编程规范
    位运算相关规律
    十进制转二进制
    递归理解
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14344308.html
Copyright © 2011-2022 走看看