• YbtOJ#763攻城略池【线段树合并】


    正题

    题目链接:http://www.ybtoj.com.cn/problem/763


    题目大意

    给出\(n\)个点的一棵树,每个\(d_i=0\)的点每秒会产生一个士兵往根节点走,走到一个节点让一个节点\(d_i\)减一(为\(0\)就不管)。

    求需要多久才能让所有点的\(d\)值变为\(0\)

    \(1\leq n\leq10^5,1\leq d_i\leq 10^8\)


    解题思路

    考虑求出每个点\(d_i\)值变成\(0\)的时间\(t_i\)

    对于一个节点\(x\)\(dis_x\)表示根节点到\(x\)的距离,那么它在时刻\(T\)时的减少数量是

    \[\sum_{y\in subtree_x}max\{T-t_y-dis_y+dis_x,0\} \]

    我们可以每次把新得到的\(t_y-dis_y\)压入线段树,然后每次合并上去后再在线段树上面二分出答案。

    时间复杂度\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e5+10,inf=2e8;
    struct node{
    	ll to,next,w;
    }a[N<<1];
    ll n,tot,cnt,ans,ls[N],d[N],t[N],rt[N],dep[N];
    void addl(ll x,ll y,ll w){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;a[tot].w=w;
    	return;
    }
    struct SegTree{
    	ll w[N<<6],c[N<<6],ls[N<<6],rs[N<<6];
    	void Change(ll &x,ll L,ll R,ll pos){
    		if(!x)x=++cnt;w[x]+=pos;c[x]++;
    		if(L==R)return;ll mid=(L+R)>>1;
    		if(pos<=mid)Change(ls[x],L,mid,pos);
    		else Change(rs[x],mid+1,R,pos);
    		return;
    	}
    	ll Ask(ll x,ll L,ll R,ll k,ll zc,ll zw){
    		if(L==R)return L;
    		ll mid=(L+R)>>1,tmp=mid*(c[ls[x]]+zc)-w[ls[x]]-zw;
    		if(tmp>=k)return Ask(ls[x],L,mid,k,zc,zw);
    		return Ask(rs[x],mid+1,R,k,zc+c[ls[x]],zw+w[ls[x]]);
    	}
    	ll Merge(ll x,ll y,ll l,ll r){
    		if(!x||!y)return x+y;
    		w[x]=w[x]+w[y];c[x]=c[x]+c[y];
    		if(l==r)return x;ll mid=(l+r)>>1;
    		ls[x]=Merge(ls[x],ls[y],l,mid);
    		rs[x]=Merge(rs[x],rs[y],mid+1,r);
    		return x;
    	}
    }T;
    void dfs(ll x,ll fa){
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		dep[y]=dep[x]+a[i].w;dfs(y,x);
    		rt[x]=T.Merge(rt[x],rt[y],0,inf);
    	}
    	t[x]=max(0ll,T.Ask(rt[x],0,inf,d[x],0,0)-dep[x]);
    	T.Change(rt[x],0,inf,t[x]+dep[x]);
    	ans=max(ans,t[x]);return;
    }
    signed main()
    {
    //	freopen("conquer.in","r",stdin);
    //	freopen("conquer.out","w",stdout);
    	scanf("%lld",&n);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld",&d[i]);
    	for(ll i=1;i<n;i++){
    		ll x,y,w;
    		scanf("%lld%lld%lld",&x,&y,&w);
    		addl(x,y,w);addl(y,x,w);
    	}
    	dfs(1,1);
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    次小生成树(SST)
    传纸条(scrip)
    动态规划练习5
    动态规划练习4
    整数的lqp拆分
    [HNOI2002]跳蚤
    BZOJ1803: Spoj1487 Query on a tree III
    51nod-1526: 分配笔名
    51nod-1615: 跳跃的杰克
    BZOJ2588: Spoj 10628. Count on a tree
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14425808.html
Copyright © 2020-2023  润新知