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

http://www.lydsy.com/JudgeOnline/problem.php?id=3924
抓紧时间补上以前忘写的博客
先考虑如何求出对于一个点,其它所有点到它的带权距离和,显然用树分治结构就可以动态维护,查询复杂度logn。由于时限宽松,可以考虑每次利用分治从头暴力求重心,方法是从根开始判断是否存在一个方向使得移动过去更优,有的话就跳到那层分治结构上。总复杂度n*log^2(n)

#include<cstdio>
#include<algorithm>
#define gm 100001
using namespace std;
const size_t str=1<<16;
typedef long long ll;
struct Reader
{
    char buf[str],*s,*t;
    Reader():s(),t(),buf(){}
    inline char pick()
    {
        return (s==t)?(t=buf+fread(s=buf,1,str,stdin),*s++):(*s++);
    }
    Reader& operator >> (int &x)
    {
        register char c;
        register bool negtive=0;
        do c=pick(); while((c<'0'||c>'9')&&c!='-');
        if(c=='-') negtive=1,c=pick();
        x=0;
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=pick();
        return x=negtive?-x:x,*this;
    }
}cin;
struct Writer
{
    char buf[str],*s,*t;
    Writer():s(buf),t(buf+str),buf(){}
    ~Writer(){fwrite(buf,1,s-buf,stdout);}
    inline void echo(char c)
    {
        (s==t)?(fwrite(s=buf,1,str,stdout),*s++=c):(*s++=c);
    }
    Writer& operator << (ll x)
    {
        if(!x) return echo('0'),*this;
        register char a[21],t=1;
        while(x) a[t++]=x%10,x/=10;
        while(--t) echo(a[t]+'0');
        return *this;
    }
    Writer& operator << (const char* s)
    {
        while(*s) echo(*s++);
        return *this;
    }
}cout;
const char *endl="\n";
struct e
{
    int t;
    e *n;
    int c;
    e(int t,e *n,int c):t(t),n(n),c(c){}
}*f[gm],*g[gm];
int n,q;
namespace RMQ
{
    int log[gm<<1];
    int dep[gm];
    int dis[gm];
    int pos[gm];
    int a[gm<<1][17];
    int top=0;
    void dfs(int x,int from)
    {
        a[pos[x]=++top][0]=x;
        dep[x]=dep[from]+1;
        for(e *i=f[x];i;i=i->n)
        {
            if(i->t==from) continue;
            dis[i->t]=dis[x]+i->c;
            dfs(i->t,x);
            a[++top][0]=x;
        }
    }
    inline int min(int a,int b)
    {
        return dep[a]<dep[b]?a:b;
    }
    void init()
    {
        dfs(1,0);
        for(int i=2;i<=top;++i)
        {
            log[i]=log[i>>1]+1;
        }
        for(int i=1;i<=log[top];++i)
        {
            for(int j=1;j+(1<<i-1)<=top;++j)
            {
                a[j][i]=min(a[j][i-1],a[j+(1<<i-1)][i-1]);
            }
        }
    }
    int LCA(int x,int y)
    {
        x=pos[x],y=pos[y];
        if(y<x) x^=y^=x^=y;
        int len=log[y-x+1];
        return min(a[x][len],a[y-(1<<len)+1][len]);
    }
    int distance(int x,int y)
    {
        return dis[x]+dis[y]-(dis[LCA(x,y)]<<1);
    }
}
namespace DAC
{
    ll self[gm],tof[gm],sum[gm];
    bool ban[gm];
    int _sz[gm],sz[gm],fa[gm];
    int size(int x,int from)
    {
        int &res=_sz[x]=1;
        for(e *i=f[x];i;i=i->n)
        {
            if(i->t==from||ban[i->t]) continue;
            res+=size(i->t,x);
        }
        return res;
    }
    int centre(int x,int from,int lim)
    {
        for(e *i=f[x];i;i=i->n)
        {
            if(i->t==from||ban[i->t]) continue;
            if(_sz[i->t]>lim>>1) return centre(i->t,x,lim);
        }
        return x;
    }
    int solve(int x)
    {
        int temp=size(x,0);
        x=centre(x,0,temp);
        sz[x]=temp;
        ban[x]=1;
        for(e *i=f[x];i;i=i->n)
        {
            if(ban[i->t]) continue;
            int y=solve(i->t);
            fa[y]=x;
            g[x]=new e(y,g[x],(int)i);
        }
        return x;
    }
    int root;
    void init()
    {
        root=solve(1);
    }
    void cast(int u,int e)
    {
        sum[u]+=e;
        for(int i=u;fa[i];i=fa[i])
        {
            int j=fa[i];
            ll dis=RMQ::distance(u,j);
            self[j]+=dis*e;
            tof[i]+=dis*e;
            sum[j]+=e;
        }
    }
    ll get(int x)
    {
        ll res=self[x];
        for(int i=x;fa[i];i=fa[i])
        {
            int j=fa[i];
            ll path=self[j]-tof[i];
            ll val=sum[j]-sum[i];
            res+=path+val*RMQ::distance(x,j);
        }
        return res;
    }
    ll query(int x=root)
    {
        ll kre=get(x);
        for(e *i=g[x];i;i=i->n)
        {
            ll sre=get(((e*)(i->c))->t);
            if(sre<kre) return query(i->t);
        }
        return kre;
    }
}
int main()
{
    cin>>n>>q;
    for(int i=1;i<n;++i)
    {
        int a,b,c;
        cin>>a>>b>>c;
        f[a]=new e(b,f[a],c);
        f[b]=new e(a,f[b],c);
    }
    RMQ::init();
    DAC::init();
    for(int i=1;i<=q;++i)
    {
        int u,e;
        cin>>u>>e;
        DAC::cast(u,e);
        cout<<DAC::query()<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值