UOJ Logo kczno1的博客

博客

[ZJOI2015]幻想乡战略游戏 的链剖+线段树做法

2017-07-11 10:40:06 By kczno1

先在线段树上二分得到 满足子树点权和>=总点权/2的 dfs序最大的点x

之后再考虑计算带权距离和,即 $ans(x)= \sum_{i} dis(i,x) * v[i]$

因为$dis(i,x)=deep[i]+deep[x]-2*deep[lca]$

所以$ans(x)=(\sum_{i} deep[i] * v[i]) + (deep[x]*\sum_{i} v[i]) -2*(\sum_{i} v[i]*deep[lca]) $

前两项直接维护

最后一个式子怎么算呢

就是每次 $v[i]$+=$x$ 就把$i$到根的路径上的点的$add$全部+=$x$

查询$ans(x)$时 就查询x到根的$\sum_{i} add[i]*(deep[i]-deep[fa[i]])$

线段树预处理区间$\sum_{i}(deep[i]-deep[fa[i]])$后可以维护

时间$O(nlog^2)$ 跑的还是挺快的

# pragma GCC optimize("O2")
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define pb push_back
const int ch_top=1e7;
char ch[ch_top],*now_r=ch-1,*now_w=ch-1;
int read()
{
    while(*++now_r<'-');
    if(*now_r=='-')
    {
        int x=*++now_r-'0';
        while(*++now_r>='0')x=x*10+*now_r-'0';
        return -x;
    }
    int x=*now_r-'0';
    while(*++now_r>='0')x=x*10+*now_r-'0';
    return x;
}
void write(ll x)
{
    static char st[20];static int top;
    if(!x)*++now_w='0';
    else
    {
        do{st[++top]='0'+x%10;}while(x/=10);
        do{*++now_w=st[top];}while(--top);
    }
    *++now_w='\n';
}

const int N=1e5+5,T=N*4;
struct edge
{
    int to,w;
};
int n,m;
vector<edge>lk[N];
int fa[N],deep[N],sz[N],son[N],top[N],dfn[N],tot;
int q[N];
void dfs(int x,int fr,int dep)
{
    fa[x]=fr;deep[x]=dep;sz[x]=1;
    vector<edge>::iterator i=lk[x].begin();
    while(i!=lk[x].end())
    {
        int y=i->to;
        if(y==fr){++i;continue;}
        dfs(y,x,dep+i->w);
        sz[x]+=sz[y];
        if(sz[y]>sz[son[x]])son[x]=y;
        ++i;
    }
}
void dfs2(int x,int ntop)
{
    q[dfn[x]=++tot]=x;top[x]=ntop;
    if(!son[x])return ;
    dfs2(son[x],ntop);
    for(vector<edge>::iterator i=lk[x].begin();i!=lk[x].end();++i)
    {
        int y=i->to;
        if(y==son[x]||y==fa[x])continue;
        dfs2(y,y);
    }
}

int x,w;
ll xs,s;
#define mid (l+r>>1)
#define cl (k*2)
#define cr (cl+1)
namespace seg
{
int ad[T],mx[T];ll s[T],xs[T];
int ql,qr;
inline void _add(int k,int x)
{
    ad[k]+=x;mx[k]+=x;
    xs[k]+=s[k]*x;
}
inline void down(int k)
{
    if(ad[k]){_add(cl,ad[k]);_add(cr,ad[k]);ad[k]=0;}
}
inline void up(int k)
{
    mx[k]=max(mx[cl],mx[cr]);
    xs[k]=xs[cl]+xs[cr];
}
void add(int k,int l,int r)
{
    if(ql<=l&&qr>=r){_add(k,w);return ;}
    down(k);
    if(ql<=mid)add(cl,l,mid);
    if(qr>mid)add(cr,mid+1,r);
    up(k);
}
void add(int l,int r)
{
    ql=l;qr=r;
    add(1,1,n);
}
void init(int k,int l,int r)
{
    if(l==r)
    {
        s[k]=deep[q[l]]-deep[fa[q[l]]];
        return ;
    }
    init(cl,l,mid);init(cr,mid+1,r);
    s[k]=s[cl]+s[cr];
}
int qiu(const ll &x)
{
    int k=1,l=1,r=n;
    while(l!=r)
    {
        down(k);
        if(mx[cr]>=x) {k=cr;l=mid+1;}
        else {k=cl;r=mid;}
    }
    return l;
}

ll ans;
void qiu(int k,int l,int r)
{
    if(ql<=l&&qr>=r){ans+=xs[k];return ;}
    down(k);
    if(ql<=mid)qiu(cl,l,mid);
    if(qr>mid)qiu(cr,mid+1,r);
}
ll qiu(int l,int r)
{
    ql=l;qr=r;ans=0;
    qiu(1,1,n);
    return ans;
}
};
void add_to_rt(int x)
{
    while(x)
    {
        int f=top[x];
        seg::add(dfn[f],dfn[x]);
        x=fa[f];
    }
}
ll qiu_s(int x)
{
    ll ans=0;
    while(x)
    {
        int f=top[x];
        ans+=seg::qiu(dfn[f],dfn[x]);
        x=fa[f];
    }
    return ans;
}

int main()
{
    freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    fread(ch,1,ch_top,stdin);
    n=read();m=read();
    rep(i,1,n-1)
    {
        int a=read(),b=read(),c=read();
        lk[a].pb((edge){b,c});lk[b].pb((edge){a,c});
    }
    dfs(1,0,0);
    dfs2(1,1);
    seg::init(1,1,n);
    while(m--)
    {
        x=read();w=read();
        add_to_rt(x);
        xs+=(ll)deep[x]*w;
        s+=w;
        x=q[seg::qiu(s+1>>1)];
        write(xs+s*deep[x]-2*qiu_s(x));
    }
    fwrite(ch,1,now_w-ch+1,stdout);
}

评论

ympc2005
orz

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。