其他分享
首页 > 其他分享> > [ZJOI2015]幻想乡战略游戏

[ZJOI2015]幻想乡战略游戏

作者:互联网

https://www.luogu.org/problemnew/show/P3345

动态点分治

考虑到带权重心一定在当前点到距离与权重总和更小的方向上(没有则当前点为重心),并且这个方向是唯一的,因此可以每次修改都这样移动,把重心找出。

然而直接移动就是直接暴力,需要更优雅的做法。

考虑在子树中移动,可以使用动态点分治的点分树,这样树高只有logn,就可以在每层之间移动重心。

那么只需要想办法求出所有带权点到某个点的距离,并支持修改即可。

由于一个子树以外的点到子树中某点距离可以看作先到根,再从根到该点,所以可以考虑容斥的方法。

然后维护两个数组即可。由于保证了每个点的度数不超过20,因此每次暴力移动重心,直到所有子节点都比它大为止,此时当前点就是重心。

时间复杂度O(20nlog2n)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100005;
int n,q,i,j,k,l,x,y,h[2*N],lg[2*N],mnpt[2*N][20],num,st[N],ed[N],head[N],adj[N*2],nxt[N*2],len[N*2],sz[N],rt,w[N],tot,Head[N],Adj[N*2],Nxt[N*2],Fa[N],dep[N],P[N][20],Dep[N],to[N*2];
ll sum[N],sumx[N],sumfa[N],dis[N];
bool v[N];
inline void Read(int &x)
{
    char c;
    int t;
    while((c=getchar())!='-'&&(c<'0'||c>'9'));
    if(c=='-')
        x=0,t=-1;
    else
        x=c-'0',t=1;
    while((c=getchar())>='0'&&c<='9')
        x=x*10+c-'0';
    x*=t;
}
void Dfs(int x,int dad)
{
    sz[x]=1;
    w[x]=-1;
    for(int y=head[x];y;y=nxt[y])
        if(adj[y]!=dad&&!v[adj[y]])
        {
            Dfs(adj[y],x);
            sz[x]+=sz[adj[y]];
            w[x]=max(w[x],sz[adj[y]]);
        }
    w[x]=max(w[x],tot-sz[x]);
    if(rt==-1||w[x]<w[rt])
        rt=x;
}
void addedge(int u,int v)
{
    Adj[++l]=v;
    Nxt[l]=Head[u];
    Head[u]=l;
    Fa[v]=u;
}
int dfs(int x,int size)
{
    tot=size,rt=-1;
    Dfs(x,-1);
    v[rt]=true;
    int rtn=rt;
    for(int y=head[rt];y;y=nxt[y])
        if(!v[adj[y]])
            addedge(rtn,to[y]=dfs(adj[y],sz[adj[y]]));
    return rtn;
}
void work(int x,int dad)
{
    h[++num]=x;
    st[x]=ed[x]=num;
    for(int y=head[x];y;y=nxt[y])
    if(adj[y]!=dad)
    {
        dep[adj[y]]=dep[x]+1;
        dis[adj[y]]=dis[x]+len[y];
        work(adj[y],x);
        h[++num]=x;
        ed[x]=num;
    }
}
int lca(int a,int b)
{
    if(st[a]>ed[b])
        swap(a,b);
    int i=lg[ed[b]-st[a]+1];
    if(dep[mnpt[st[a]][i]]<dep[mnpt[ed[b]-(1<<i)+1][i]])
        return mnpt[st[a]][i];
    else
        return mnpt[ed[b]-(1<<i)+1][i];
}
ll dist(int x,int y)
{
    return dis[x]+dis[y]-2*dis[lca(x,y)];
}
ll dzx(int x)
{
    ll rtn=sumx[x];
    int y=x;
    while(Fa[y])
    {
        rtn=rtn+sumx[Fa[y]]-sumfa[y]+(sum[Fa[y]]-sum[y])*dist(Fa[y],x);//容斥
        y=Fa[y];
    }
    return rtn;
}
void Work(int x)
{
    for(int y=Head[x];y;y=Nxt[y])
    {
        Dep[Adj[y]]=Dep[x]+1;
        P[Adj[y]][0]=x;
        Work(Adj[y]);
    }
}
int main()
{
    Read(n);Read(q);
    for(i=1;i<n;++i)
    {
        Read(j);Read(k);Read(len[i*2]);
        adj[i*2-1]=k;
        nxt[i*2-1]=head[j];
        head[j]=i*2-1;
        adj[i*2]=j;
        nxt[i*2]=head[k];
        head[k]=i*2;
        len[i*2-1]=len[i*2];
    }
    rt=dfs(1,n);
    work(1,-1);
    for(i=1;i<=num;++i)
    {
        mnpt[i][0]=h[i];
        if(i==1)
            lg[i]=0;
        else if(i==(1<<(lg[i-1]+1)))
            lg[i]=lg[i-1]+1;
        else
            lg[i]=lg[i-1];
    }
    for(i=1;(1<<i)<=num;++i)
        for(j=1;j+(1<<i)-1<=num;++j)
            if(dep[mnpt[j][i-1]]<dep[mnpt[j+(1<<(i-1))][i-1]])
                mnpt[j][i]=mnpt[j][i-1];
            else
                mnpt[j][i]=mnpt[j+(1<<(i-1))][i-1];
    Work(1);
    for(i=1;(1<<i)<n;++i)
        for(j=1;j<=n;++j)
            if(P[j][i-1])
                P[j][i]=P[P[j][i-1]][i-1];
    while(q--)
    {
        Read(i);Read(j);
        k=i;
        while(k)
        {
            sum[k]+=j;
            sumx[k]+=1ll*dist(k,i)*j;
            if(Fa[k])
                sumfa[k]+=1ll*dist(Fa[k],i)*j;
            k=Fa[k];
        }
        x=rt;
        while(1)
        {
            ll phh=dzx(x);
            for(y=head[x];y;y=nxt[y])//题目保证度数不超过20,所以枚举出边的复杂度正确
            {
                if(dzx(adj[y])<phh)
                {
                    x=to[y];
                    y=1;
                    break;
                }
            }
            if(!y)
                break;
        }
        printf("%lld\n",dzx(x));
    }
    return 0;
}

 

标签:幻想,移动,20,游戏,重心,int,st,ZJOI2015,include
来源: https://www.cnblogs.com/pthws/p/11219442.html