bzoj3924: [Zjoi2015]幻想乡战略游戏 //动态点分治

bzoj3924: [Zjoi2015]幻想乡战略游戏


题意

给出一棵N(<=1e5)个点的树。
特殊性质:每个点度数不超过20。
M(<=1e5)次操作,支持更改一个点的点权,每次操作后输出∑(每个点的点权*该点到带权重心距离)。


题解

关于找带权重心:
规定sum[i]表示点i子树的点权和。
如果当前在x,重心在son[x]的子树里,当且仅当sum[x]<2*sum[son[x]]
然后我们直接走过去就可以了。
多个重心的话 这样说好像并不严谨 算了感性理解吧
我们点分一发。
查询的时候从分治根开始走,枚一下所有出边。
如果发现当前重心在该边指向的分治子树里,就走到分治子树根的位置,重复上述过程。
这个复杂度是log*20的。
答案怎么统计呢?
从x向其相连的y所在子树走的时候,把x子树中除y所在子树外的点权压到y上。
这一步和直接修改点权的更新一样 都是log的。
所以就是O(nlog^2n+nlogn*20)


代码

#include<bits/stdc++.h>
#define N 100005
#define L 17
using namespace std;
typedef long long ll;
int n,m,w,rt,dep[N][L],f[N],siz[N],dp[N],ch[N<<1],
to[N<<1],hd[N<<1],lk[N],len[N<<1],stu[L],stv[L],stw[L],tot,cnt;
ll mul[N],sum[N],de[N],ans,tmp;
bool vis[N];
void ini(int x,int lst)
{
    siz[x]=1;
    for(int k,i=lk[x];i;i=hd[i])
    if((k=to[i])^lst)
    ini(k,x),siz[x]+=siz[k];
}
void dfs(int x,int y,int l)
{
    siz[x]=1;
    for(int k,i=lk[x];i;i=hd[i])
    if(((k=to[i])^y)&&!vis[k])
    dep[k][l]=dep[x][l]+len[i],
    dfs(k,x,l),siz[x]+=siz[k];
}
int getr(int x,int y,int nn)
{
    for(int k,i=lk[x];i;i=hd[i])
    if(((k=to[i])^y)&&siz[k]>nn&&!vis[k])
    return getr(k,x,nn);
    return x;
}
int build(int x,int fa)
{
    vis[x=getr(x,x,siz[x]>>1)]=1;
    if(fa)dp[x]=dp[f[x]=fa]+1;
    dfs(x,x,dp[x]);
    for(int i=lk[x];i;i=hd[i])
    if(!vis[to[i]])
    ch[i]=build(to[i],x);
    return x;
}
inline void upd(int u,int v,int y)
{
    for(int i=u;;i=f[i])
    {
        sum[i]+=v,mul[i]+=(ll)v*dep[u][dp[i]];
        if(f[i]==y)break;
        de[i]+=(ll)v*dep[u][dp[i]-1];
    }
}
inline void add(int u,int v)
{to[++cnt]=v,hd[cnt]=lk[u],len[cnt]=w,lk[u]=cnt;}
int u,v;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    scanf("%d%d%d",&u,&v,&w),
    add(u,v),add(v,u);
    ini(1,0),rt=build(1,0);
    while(m--)
    {
        scanf("%d%d",&u,&v);
        upd(u,v,0);
        tot=tmp=0;
        for(int i=rt;;)
        {
            tmp+=mul[u=i];
            for(int k,j=lk[i];j;j=hd[j])
            if((k=ch[j])&&sum[i]<(sum[k]<<1))
            {
                tmp+=(sum[i]-sum[k])*len[j]-de[k];
                upd(stu[tot]=to[j],stv[tot]=sum[i]-sum[k],stw[tot]=i),tot++;
                i=k;
            }
            if(u==i)break;
        }
        printf("%lld\n",tmp);
        while(tot--)
        upd(stu[tot],-stv[tot],stw[tot]);
    }
}

猜你喜欢

转载自blog.csdn.net/Starria/article/details/79051992