bzoj 3924: [Zjoi2015]幻想乡战略游戏 动态树分治

来源:互联网 发布:mdict for mac 编辑:程序博客网 时间:2024/05/16 08:33

题意

给出一棵树,边有边权。每次操作要求修改一个点的点权d,然后求一个点p使得dis(p,i)d[i]最小。
n,q<=100000

分析

实际上也就是动态维护整棵树的带权重心。

考虑先建出分治树,每个点维护三个值sd[x]表示该点子树内的点权和,sv[x]表示该点子树内所有点到该点的带权距离,tofa[x]表示该点子树内所有点到其父亲的带权距离。这里的子树和父亲都是指分治树上的。
修改的话就暴力沿着父亲往上跳,修改路径上的值即可。
查询的话,网上有很多题解说的都是从分治树的根节点开始一直往子树走,每次都要换根然后再复原。蒟蒻表示看的瑟瑟发抖一脸懵逼。
恩实际上我们可以首先找到带权重心,然后把带权重心到分治树的根的路径跑一遍就可以得到答案了。至于具体怎么求就自己yy好啦。
那我们怎么求带权重心呢?
首先我们把原树看做有根树,然后用dfs序+树状数组来维护每个点在原树中的子树内点权和。
然后我们从树分治的根节点开始,每次枚举该节点在原树中的所有出边,如果沿着这条边走更优的话,就从该节点走到该边在分治树中所属的子树内。

这样的复杂度严格来讲是O(nlog2)的,但实际跑的飞快,估计是树状数组常数小的缘故。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;typedef long long LL;const int N=100005;int n,q,cnt,last[N],root,a1,a[N*2],dep[N],pos[N],sd[N],ls[N],lg[N*2],rmq[N*2][25],size[N],mx[N],tot,fa[N],sum,c[N],dfn[N],tim,mndfn[N],mxdfn[N];LL sv[N],tofa[N];struct edge{int to,len,next,to1;}e[N*3];bool vis[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void addedge(int u,int v,int len){    e[++cnt].to=v;e[cnt].len=len;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].to=u;e[cnt].len=len;e[cnt].next=last[v];last[v]=cnt;}void addedge1(int u,int v,int len){    e[++cnt].to=v;e[cnt].len=len;e[cnt].next=ls[u];ls[u]=cnt;}void dfs(int x,int fa){    pos[x]=++a1;a[a1]=dep[x];dfn[++tim]=x;mndfn[x]=mxdfn[x]=tim;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].to==fa) continue;        dep[e[i].to]=dep[x]+e[i].len;        dfs(e[i].to,x);        a[++a1]=dep[x];    }    mxdfn[x]=tim;}void ins(int x,int y){    while (x<=n) c[x]+=y,x+=x&(-x);}int get_w(int x){    int l=mndfn[x]-1,r=mxdfn[x];    int ans=0;    while (r) ans+=c[r],r-=r&(-r);    while (l) ans-=c[l],l-=l&(-l);    return ans;}int get_len(int x,int y){    int l=pos[x],r=pos[y];    if (l>r) swap(l,r);    int w=lg[r-l+1];    return dep[x]+dep[y]-2*min(rmq[l][w],rmq[r-(1<<w)+1][w]);}void get_root(int x,int fa){    size[x]=1;mx[x]=0;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].to==fa||vis[e[i].to]) continue;        get_root(e[i].to,x);        size[x]+=size[e[i].to];        mx[x]=max(mx[x],size[e[i].to]);    }    mx[x]=max(mx[x],tot-size[x]);    if (!root||mx[x]<mx[root]) root=x;}void build(int x){    vis[x]=1;    for (int i=last[x];i;i=e[i].next)    {        if (vis[e[i].to]) continue;        root=0;tot=size[e[i].to];        get_root(e[i].to,x);        e[i].to1=root;        fa[root]=x;addedge1(x,root,get_len(x,root));        build(root);    }}void modify(int x,int y,int z){    sd[x]+=z;sv[x]+=(LL)z*get_len(x,y);    if (!fa[x]) return;    tofa[x]+=(LL)z*get_len(y,fa[x]);    modify(fa[x],y,z);}int find(int x){    for (int i=last[x];i;i=e[i].next)    {        int w;        if (dep[e[i].to]>dep[x]) w=get_w(e[i].to);        else w=sum-get_w(x);        if (w*2>sum) return find(e[i].to1);    }    return x;}LL query(int x,int y,int son){    if (!x) return 0;    LL ans=0;    if (x==y) ans=sv[x];    else ans=sv[x]-tofa[son]+(LL)get_len(x,y)*(sd[x]-sd[son]);    return ans+query(fa[x],y,x);}int main(){    n=read();q=read();    for (int i=1;i<n;i++)    {        int x=read(),y=read(),z=read();        addedge(x,y,z);    }    dfs(1,0);    for (int i=1;i<=a1;i++) lg[i]=log(i)/log(2),rmq[i][0]=a[i];    for (int j=1;j<=lg[a1];j++)        for (int i=1;i<=a1-(1<<j)+1;i++)            rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);    root=0;tot=n;    get_root(1,0);    int R=root;    build(root);    while (q--)    {        int x=read(),y=read();sum+=y;        modify(x,x,y);        ins(mndfn[x],y);        int ansp=find(R);        printf("%lld\n",query(ansp,ansp,0));    }    return 0;}
0 0
原创粉丝点击