1. 程式人生 > >【Luogu3676】小清新數據結構題(動態點分治)

【Luogu3676】小清新數據結構題(動態點分治)

證明 路徑 ont getchar ostream fin org 線段 fine

【Luogu3676】小清新數據結構題(動態點分治)

題面

洛谷

題解

先扯遠點,這題我第一次看的時候覺得是一個樹鏈剖分+線段樹維護。
做法大概是這樣:
我們先以任意一個點為根,把當前點看成是一棵有根樹。比方說以\(1\)為根。
那麽,在詢問以\(p\)為根的時候的答案,我們看看哪些子樹發生了變化。
發現真正會產生變化的只有\(1..p\)這條鏈上的所有點,其它點的貢獻和以\(1\)為根時的貢獻是一樣的。
考慮這條鏈上的所有點的貢獻變成了什麽,假設這條鏈上的所有點分別是\(c_1,c_2...,c_n\)
那麽\(c_i\)的子樹和是\(\sum Val-\sum _{SubTree c_{i+1}}Val\)


也就是整棵樹的所有權值和減去這條鏈上的那個兒子的子樹和。
因為最終的貢獻有個平方,所以我們維護子樹的\((\sum Val)^2\)\(\sum Val\)
修改的時候把平方式拆開來維護就好了
更喪一點,你可以把\([(\sum val)^2,\sum val , c]\)看成一個矩陣,每次修改相當於一個矩陣乘法
其中\(c=1\)
這樣子就可以用線段樹+樹鏈剖分來維護了。
這樣應該是對的吧,我沒有實踐,純屬yy

以上內容都是廢話,可以當做沒有看見

還是一樣,先確定為一棵有根樹,
\(s_i\)表示以\(i\)為根的的子樹的權值和,\(w\)為整棵樹的權值和。
我們要求的東西是\(\sum_{i=1}^n s_i^2\)


發現\(\sum_{i=1}^n s_i(w-s_i)\)是定值。
證明是這樣的,我們考慮一下上述式子是個什麽東西,即在任意一個點的子樹內和子樹外中選擇一個點然後求他們的乘積和。
那麽,對於任意一對\((u,v)\),他們產生的貢獻的次數顯然是枚舉路徑上除了\(lca\)外的任意一個點進行選擇,那麽路徑上的點數是定值,所以上述式子是定值。
那麽這就很好辦了,\(w\)是很容易維護的,所以我們只需要維護出\(\sum_{i=1}^ns_i\)
就有\(\sum_{i=1}^ns_i^2=w\sum_{i=1}^ns_i-P\),其中\(P\)就是這個定值。
這樣子以來,所有的東西都可以利用動態點分治維護即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,V[MAX];
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
/********************************************************************/
int size[MAX],dfn[MAX],top[MAX],dep[MAX],fa[MAX],tim,hson[MAX];
void dfs1(int u,int ff)
{
    fa[u]=ff;size[u]=1;dep[u]=dep[ff]+1;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;
        dfs1(v,u);size[u]+=size[v];
        if(size[v]>size[hson[u]])hson[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp;
    if(hson[u])dfs2(hson[u],tp);
    for(int i=h[u];i;i=e[i].next)
        if(e[i].v!=fa[u]&&e[i].v!=hson[u])
            dfs2(e[i].v,e[i].v);
}
int LCA(int u,int v)
{
    while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
    return dep[u]<dep[v]?u:v;
}
int Dis(int u,int v){return dep[u]+dep[v]-2*dep[LCA(u,v)];}
/********************************************************************/
bool vis[MAX];
int Fa[MAX],Size,root,mx;
void Getroot(int u,int ff)
{
    size[u]=1;int ret=0;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff||vis[v])continue;
        Getroot(v,u);size[u]+=size[v];
        ret=max(ret,size[v]);
    }
    ret=max(ret,Size-size[u]);
    if(ret<mx)mx=ret,root=u;
}
void DFS(int u,int ff)
{
    vis[u]=true;Fa[u]=ff;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(vis[v])continue;
        mx=Size=size[v];
        Getroot(v,u);DFS(root,u);
    }
}
/********************************************************************/
ll P,W;
ll sum[MAX],tf[MAX],num[MAX];
void Modify(int u,int w)
{
    num[u]+=w;
    for(int i=u;Fa[i];i=Fa[i])
    {
        int d=Dis(u,Fa[i]);
        num[Fa[i]]+=w;sum[Fa[i]]+=1ll*w*d;
        tf[i]+=1ll*w*d;
    }
}
ll Query(int u)
{
    ll ret=sum[u];
    for(int i=u;Fa[i];i=Fa[i])
    {
        int d=Dis(u,Fa[i]);
        ret+=1ll*d*(num[Fa[i]]-num[i]);
        ret+=sum[Fa[i]]-tf[i];
    }
    return ret;
}
void dfs(int u,int ff)
{
    size[u]=V[u];
    for(int i=h[u];i;i=e[i].next)
        if(e[i].v!=ff)dfs(e[i].v,u),size[u]+=size[e[i].v];
    P+=1ll*size[u]*(W-size[u]);
}
/********************************************************************/
int main()
{
    n=read();m=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        Add(u,v);Add(v,u);
    }
    for(int i=1;i<=n;++i)V[i]=read();
    dfs1(1,0);dfs2(1,1);
    Size=mx=n;Getroot(1,0);DFS(root,0);
    for(int i=1;i<=n;++i)Modify(i,V[i]),W+=V[i];
    dfs(1,0);
    while(m--)
    {
        int opt=read(),x=read(),y;
        if(opt==1)
        {
            y=read();Modify(x,y-V[x]);W+=y-V[x];
            P+=(y-V[x])*Query(x);V[x]=y;
        }
        else printf("%lld\n",(Query(x)+W)*W-P);
    }
    return 0;
}

【Luogu3676】小清新數據結構題(動態點分治)