• [ZJOI2015] 幻想乡战略游戏


    令1为树根,T=Σd[i],sum[x]是子树x中的d之和,dis[x]是节点x的带权深度。

    对于节点x的儿子y,边长为w,如果y比x更优,则(T-sum[y])*w-sum[y]*w<=0即sum[y]>=T/2。

    换句话说,若存在sum[y]>T/2,显然y更优,且y只存在一个(对于x);若存在sum[y]=T/2,(1个或2个),因为y的子树内都不存在sum>T/2的点,所以y就是一个最优解,不必递归地做。

    考虑一类暴力求出最优点:从根出发依次进入sum>T/2的儿子,最深的那个点即为带权重心。这可以树剖维护sum/区间最大值,如果右半区间(较深)存在>=T/2,即向右跳转。

    设已经求出了最优点R,答案是Σ(d[i]*dis[i]+d[i]*dis[R]-2*d[i]*dis[lca(R,i)]),套路地维护就行了。

    意外的简单?

    #include <bits/stdc++.h>
    #define ll long long 
    #define trvl(x,i,y) for(int i=ehd[x],y; y=to[i],i; i=lst[i]) 
    #define ls (x<<1)
    #define rs (x<<1|1)
    using namespace std;
    
    const int N=1e5+10;
    
    int n,m;
    int ehd[N],to[N<<1],len[N<<1],lst[N<<1];
    int fa[N],siz[N],son[N],top[N],id[N],dfn[N];
    ll S,T,dis[N];
    
    void insert(int x,int y,int w) {
    	static int cnt=0;
    	to[++cnt]=y,len[cnt]=w,lst[cnt]=ehd[x],ehd[x]=cnt;
    	to[++cnt]=x,len[cnt]=w,lst[cnt]=ehd[y],ehd[y]=cnt;
    }
    void dfs1(int x,int p) {
    	fa[x]=p; siz[x]=1;
    	trvl(x,i,y) if(y!=p) {
    		dis[y]=dis[x]+len[i];
    		dfs1(y,x);siz[x]+=siz[y];
    		if(siz[y]>siz[son[x]]) son[x]=y;
    	}
    }
    void dfs2(int x,int t) {
    	static int cnt=0;
    	top[dfn[id[x]=++cnt]=x]=t;
    	if(son[x]) dfs2(son[x],t);
    	trvl(x,i,y) if(son[x]!=y&&fa[x]!=y) dfs2(y,y);
    }
    
    int tag[N<<2],mx[N<<2];
    ll c[N<<2],s[N<<2];
    
    void psd(int x) {
    	if(!tag[x]) return;
    	c[ls]+=s[ls]*tag[x],mx[ls]+=tag[x],tag[ls]+=tag[x];
    	c[rs]+=s[rs]*tag[x],mx[rs]+=tag[x],tag[rs]+=tag[x];
    	tag[x]=0;
    }
    void upd(int x) {
    	mx[x]=max(mx[ls],mx[rs]);
    	c[x]=c[ls]+c[rs];
    }
    void build(int x,int l,int r) {
    	if(l==r) {
    		s[x]=dis[dfn[l]]-dis[fa[dfn[l]]];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(ls,l,mid);
    	build(rs,mid+1,r);
    	s[x]=s[ls]+s[rs];
    }
    int find(int x,int l,int r) {
    	if(l==r) return dfn[l];
    	int mid=(l+r)>>1; psd(x);
    	if(mx[rs]*2>T) return find(rs,mid+1,r);
    	return find(ls,l,mid);
    }
    void mdf(int x,int l,int r,int L,int R,ll w) {
    	if(L<=l&&r<=R) {
    		c[x]+=s[x]*w,mx[x]+=w,tag[x]+=w;
    		return;
    	}
    	int mid=(l+r)>>1; psd(x);
    	if(L<=mid) mdf(ls,l,mid,L,R,w);
    	if(mid<R) mdf(rs,mid+1,r,L,R,w);
    	upd(x);
    }
    ll qry(int x,int l,int r,int L,int R) {
    	if(L<=l&&r<=R) return c[x];
    	int mid=(l+r)>>1; ll c=0; psd(x);
    	if(L<=mid) c+=qry(ls,l,mid,L,R);
    	if(mid<R) c+=qry(rs,mid+1,r,L,R);
    	return c;
    }
    void modify(int x,int w) {
    	for(; x; x=fa[top[x]]) mdf(1,1,n,id[top[x]],id[x],w);
    }
    ll query(int x,ll w=0) {
    	for(; x; x=fa[top[x]]) w+=qry(1,1,n,id[top[x]],id[x]);
    	return w;
    }
    
    int main() {
    	scanf("%d%d",&n,&m);
    	for(int x,y,w,i=n; --i; ) {
    		scanf("%d%d%d",&x,&y,&w);
    		insert(x,y,w);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	build(1,1,n);
    	for(int x,w; m--; ) {
    		scanf("%d%d",&x,&w);
    		modify(x,w);
    		S+=dis[x]*w;
    		T+=w;
    		x=find(1,1,n);
    		printf("%lld
    ",S+T*dis[x]-2*query(x));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    C#创建自定义配置节
    linux下安装nginx
    linux查看防火墙状态和对外开放的端口状态
    js 获取二级域名
    .net core 获取本地ip及request请求端口
    《趣谈 Linux 操作系统》学习笔记(二):对 Linux 操作系统的理解
    《趣谈 Linux 操作系统》学习笔记(一):为什么要学 Linux 及学习路径
    Redis Cluster集群
    Redis的主从复制与Redis Sentinel哨兵机制
    Redis持久化方案
  • 原文地址:https://www.cnblogs.com/nosta/p/10996764.html
Copyright © 2020-2023  润新知