zoukankan      html  css  js  c++  java
  • 并不对劲的bzoj3924:loj2135:p3345:[ZJOI2015]幻想乡战略游戏

    题目大意

    有一棵有(n)((nleq10^5))个点的树,有点权(d_i)、边权(c_i),有(m)((mleq10^5))次操作。
    每次操作给定(x,y(yin[-1000,1000])),将点(x)的点权+y,要找到一个点,使所有点的 点权乘到这个点的距离 之和最小,输出最小的和。

    题解

    这题相当于求带权重心。设点(x)的子树点权和为(s_x),子树内所有点到它的距离和为(S_x),点1为根。
    有一种(Theta(n))求带权重心的方法:
    先暂时地假设根就是“重心”,考虑能不能改变“重心”使所有点((点权) imes(到这个点的距离))最小:对于一个儿子(x),假设根到它的距离是(w_x),把“重心”移过去,会使该儿子子树内包括该儿子所有点到“重心”的距离(-w_x),其他点(+w_x),所以当((该儿子子树中包括该儿子的点权和)geq(其他点的点权和))(s_x imes 2geq s_1)时,该儿子比根更优。可以发现这种儿子至多有一个。重复该过程,直到“重心”无处可移。
    由上述过程可以得出,要找的点是深度最深的满足(S_xgeq S_1-S_x)(S_x imes2leq S_1)(1)的点。
    通过移动“重心”的过程可以知道,满足(1)的点要么就是点1,要么就是一条从根出发的直链。从根开始dfs时,无论先走哪个儿子,都有每个点在dfs序中肯定在它的祖先的后面。
    所有这相当于求dfs序中最靠后的满足(1)的点。
    可以树剖维护dfs序的区间最大(S_x),再二分。

    代码
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #define rep(i,x,y) for(register int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
    #define LL long long
    #define maxn 100005
    #define maxm (maxn<<1)
    #define view(u,k) for(int k=fir[u];k!=-1;k=nxt[k])
    #define ls (u<<1)
    #define rs (u<<1|1)
    #define mi (l+r>>1)
    #define lt (x&-x)
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
        return x*f;
    }
    void write(LL x)
    {
        if(x==0){putchar('0'),putchar('
    ');return;}
        int f=0;char ch[20];
        if(x<0)putchar('-'),x=-x;
        while(x)ch[++f]=x%10+'0',x/=10;
        while(f)putchar(ch[f--]);
        putchar('
    ');
        return;
    }
    LL up[maxn<<2],dn[maxn<<2],w[maxm],sumall,sz[maxn<<2],mksz[maxn<<2],dis[maxn],sumlen,tr[maxn<<2],ad[maxn<<2],mk[maxn<<2];
    int n,q,fir[maxn],nxt[maxm],v[maxm],cnt,tofa[maxn];
    int dfn[maxn],to[maxn],tim,dep[maxn],siz[maxn],son[maxn],fa[maxn],top[maxn];
    void ade(int u1,int v1,int w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;}
    void getson(int u)
    {
        siz[u]=1;
        view(u,k)if(v[k]!=fa[u])
        {
            fa[v[k]]=u,dep[v[k]]=dep[u]+1,dis[v[k]]=dis[u]+w[k],tofa[v[k]]=w[k],getson(v[k]),siz[u]+=siz[v[k]];
            if(siz[son[u]]<siz[v[k]])son[u]=v[k];
        }
    }
    void gettop(int u,int anc)
    {
        dfn[u]=++tim,to[tim]=u,top[u]=anc;
        if(son[u])gettop(son[u],anc);
        view(u,k)if(v[k]!=fa[u]&&v[k]!=son[u])gettop(v[k],v[k]);
    }
    void mark(int u,int k){sz[u]+=k,mksz[u]+=k;return;}
    void mark2(int u,int k){tr[u]+=k*ad[u],mk[u]+=k;}
    void pd2(int u){if(mk[u]){mark2(ls,mk[u]),mark2(rs,mk[u]),mk[u]=0;}}
    void pd(int u){if(mksz[u])mark(ls,mksz[u]),mark(rs,mksz[u]),mksz[u]=0;}
    void build(int u,int l,int r)
    {
        if(l==r){ad[u]=tofa[to[l]];return;}
        build(ls,l,mi),build(rs,mi+1,r),ad[u]=ad[ls]+ad[rs];
    }
    void addsz(int u,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y){mark(u,k),mark2(u,k);return;}
        pd(u),pd2(u);
        if(x<=mi)addsz(ls,l,mi,x,y,k);
        if(y>mi)addsz(rs,mi+1,r,x,y,k);
        sz[u]=max(sz[ls],sz[rs]),tr[u]=tr[ls]+tr[rs];return;
    }
    LL que(int u,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return tr[u];
        pd2(u);LL res=0;
        if(x<=mi)res=que(ls,l,mi,x,y);
        if(y>mi)res+=que(rs,mi+1,r,x,y);
        return res;
    }
    int ask(int u,int l,int r)
    {
        if(l==r)return l;
        pd(u);
        if((sz[rs]<<1)>=sumall)return ask(rs,mi+1,r);
        return ask(ls,l,mi);
    }
    void addrd(int u,int k)
    {
        while(top[u]!=1)addsz(1,1,n,dfn[top[u]],dfn[u],k),u=fa[top[u]];
        addsz(1,1,n,1,dfn[u],k);
    }
    LL askrd(int u)
    {
        LL res=0;
        while(top[u]!=1)res+=que(1,1,n,dfn[top[u]],dfn[u]),u=fa[top[u]];
        res+=que(1,1,n,1,dfn[u]);return res;
    }
    int main()
    {
        memset(fir,-1,sizeof(fir));
        n=read(),q=read();
        rep(i,1,n-1){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);}
        getson(1),gettop(1,1),build(1,1,n);
        while(q--)
        {
            int x=read(),y=read();addrd(x,y),sumall+=y,sumlen+=dis[x]*y;
            int wt=to[ask(1,1,n)];LL tmp=askrd(wt);
            write(sumlen+sumall*dis[wt]-tmp*2);
        }
        return 0;
    }
    
  • 相关阅读:
    错因集锦
    组合数学12
    硬币购物
    考试套路整理
    考前模板整理
    我的友链
    P4127 [AHOI2009]同类分布
    P1836 数页码_NOI导刊2011提高(04)
    P4124 [CQOI2016]手机号码
    数位DP小结
  • 原文地址:https://www.cnblogs.com/xzyf/p/10275526.html
Copyright © 2011-2022 走看看