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


    题意:给定一棵树,每次钦定一个点为根,求以这个点为根时每个点子树权和平方之和,有单点修改.

    虽然有修改,但是没有换根操作(等于说每次询问时只换依次根)

    我们初始的时候不妨令 $1$ 为根,并考虑当根为 $x$ 时与根为 $1$ 时的差别.

    我们发现,当根为 $x$ 时,子树权和发生改变的只是 $1$ 到 $x$ 上这条链上的所有点,其余点子树权在换根后不变.

    然后我们将这条链的贡献列出来(换根前与换根后),发现满足 $ans'=ans+(len-1)tot^2-2tot sum_{i=1}^{k}sum_{i}$

    然后后面那个 $sum sum_{i}$ 用树剖/LCT/DFS序维护一下就好了. 

    #include <cstdio> 
    #include <string> 
    #include <cstring>   
    #include <algorithm>    
    #define N 200008 
    #define ll long long    
    #define lson s[x].ch[0] 
    #define rson s[x].ch[1]  
    using namespace std;     
    ll ans;  
    void setIO(string s) 
    {
        freopen((s+".in").c_str(),"r",stdin); 
        // freopen((s+".out").c_str(),"w",stdout); 
    }        
    int edges; 
    int sta[N],hd[N],to[N<<1],nex[N<<1],val[N];    
    void Add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; }  
    struct LCT 
    {  
        int ch[2],rev,f,size;   
        ll tag,sum,v;   
    }s[N];    
    int get(int x) { return s[s[x].f].ch[1]==x; }
    int Isr(int x) { return s[s[x].f].ch[0]!=x&&s[s[x].f].ch[1]!=x;}             
    void pushup(int x) 
    {
        s[x].sum=s[lson].sum+s[rson].sum+s[x].v;  
        s[x].size=s[lson].size+s[rson].size+1;   
    }
    void add(int x,ll v) 
    {
        s[x].v+=v;   
        s[x].sum+=v*1ll*s[x].size;   
        s[x].tag+=v;  
    }
    void rotate(int x) 
    {
        int old=s[x].f,fold=s[old].f,which=get(x);  
        if(!Isr(old)) s[fold].ch[s[fold].ch[1]==old]=x;   
        s[old].ch[which]=s[x].ch[which^1];      
        if(s[old].ch[which]) s[s[old].ch[which]].f=old;  
        s[x].ch[which^1]=old,s[old].f=x,s[x].f=fold;   
        pushup(old),pushup(x);  
    }    
    void pushdown(int x) 
    {
        if(s[x].tag) 
        {
            if(lson) add(lson,s[x].tag); 
            if(rson) add(rson,s[x].tag);  
            s[x].tag=0; 
        }
    }
    void splay(int x)  
    {  
        int u=x,v=0,fa; 
        for(sta[++v]=u;!Isr(u);u=s[u].f) sta[++v]=s[u].f;   
        for(;v;--v) pushdown(sta[v]);  
        for(u=s[u].f;(fa=s[x].f)!=u;rotate(x))   
            if(s[fa].f!=u) 
                rotate(get(fa)==get(x)?fa:x);   
    }
    void Access(int x) 
    {
        for(int y=0;x;y=x,x=s[x].f) 
            splay(x),rson=y,pushup(x);  
    }     
    void dfs(int u,int ff) 
    {
        s[u].f=ff;             
        s[u].v=val[u]; 
        for(int i=hd[u];i;i=nex[i]) 
            if(to[i]!=ff) dfs(to[i],u),s[u].v+=s[to[i]].v;      
        ans+=s[u].v*s[u].v;     
        pushup(u);   
    }
    int main() 
    {
        // setIO("input");  
        ll tot=0ll;  
        int i,j,n,Q,x,y;
        scanf("%d%d",&n,&Q);   
        for(i=1;i<n;++i)  scanf("%d%d",&x,&y),Add(x,y),Add(y,x); 
        for(i=1;i<=n;++i) scanf("%d",&val[i]),tot+=val[i];  
        dfs(1,0);                
        for(i=1;i<=Q;++i) 
        {
            int op; 
            scanf("%d",&op); 
            if(op==1) 
            {
                scanf("%d%d",&x,&y);             
                int d=y-val[x];     
                Access(x),splay(x);
                ans+=2ll*s[x].sum*d+1ll*s[x].size*d*d;    
                add(x,d);     
                tot+=d;   
                val[x]=y;        
    
            }
            else 
            {
                scanf("%d",&x);                          
                Access(x),splay(x);   
                int siz=s[x].size-1;   
                ll sum=s[x].sum-tot;       
                printf("%lld
    ",ans+1ll*siz*tot*tot-2ll*tot*sum);       
            }
        }
        return 0; 
    }
    

      

  • 相关阅读:
    通讯录封装实现
    简单通讯录的实现 main..h .m文件全部
    iOS 开发 OC编程 字典和集合 排序方法
    iOS 开发 OC编程 数组冒泡排序.图书管理
    iOS 开发 OC编程 属性和字符串练习
    iOS 开发 OC编程 属性和字符串
    iOS 开发 OC编程 便利构造器以及初始化方法
    iOS 开发 OC编程 方法的书写
    IOS 开发 OC编程 类和对象
    iOS 开发 c语言阶段考试题
  • 原文地址:https://www.cnblogs.com/guangheli/p/12122978.html
Copyright © 2020-2023  润新知