zoukankan      html  css  js  c++  java
  • bzoj 5212: [Zjoi2018]历史

    Description

    九条可怜是一个热爱阅读的女孩子。
    这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣。
    这个世界有n个城市,这n个城市被恰好n?1条双向道路联通,即任意两个城市都可以互相到达。同时城市1坐落在世
    界的中心,占领了这个城市就称霸了这个世界。
    在最开始,这n个城市都不在任何国家的控制之下,但是随着社会的发展,一些城市会崛起形成国家并夺取世界的
    霸权。为了方便,我们标记第i个城市崛起产生的国家为第i个国家。在第i个城市崛起的过程中,第i个国家会取得
    城市i到城市1路径上所有城市的控制权。
    新的城市的崛起往往意味着战争与死亡,若第i个国家在崛起中,需要取得一个原本被国家j(j!=i)控制的城市的控
    制权,那么国家i就必须向国家j宣战并进行战争。
    现在,可怜知道了,在历史上,第i个城市一共崛起了ai次。但是这些事件发生的相对顺序已经无从考究了,唯一
    的信息是,在一个城市崛起称霸世界之前,新的城市是不会崛起的。战争对人民来说是灾难性的。可怜定义一次崛
    起的灾难度为崛起的过程中会和多少不同的国家进行战争(和同一个国家进行多次战争只会被计入一次)。可怜想
    要知道,在所有可能的崛起顺序中,灾难度之和最大是多少。
    同时,在考古学家的努力下,越来越多的历史资料被发掘了出来,根据这些新的资料,可怜会对ai进行一些修正。
    具体来说,可怜会对ai进行一些操作,每次会将ax加上w。她希望在每次修改之后,都能计算得到最大的灾难度。
    然而可怜对复杂的计算并不感兴趣,因此她想让你来帮她计算一下这些数值。
    对题面的一些补充:
    1:同一个城市多次崛起形成的国家是同一个国家,这意味着同一个城市连续崛起两次是不会和任何国家开战的:因
    为这些城市原来就在它的控制之下。
    2:在历史的演变过程中,第i个国家可能会有一段时间没有任何城市的控制权。但是这并不意味着第i个国家灭亡了
    ,在城市i崛起的时候,第i个国家仍然会取得1到i路径上的城市的控制权

    Solution

    实际上崛起就是一个 (access) 的过程,同一 (splay) 中的城市被同一个国家占领
    要最大化的就是轻重边切换的次数
    考虑最优决策:
    每一个点被占领的次数是独立的,之和其子树内的点 (access) 的顺序有关,我们可以分开算贡献
    最优决策肯定是不断的在子树内切换,使得这个点被占领的次数尽可能多
    如果设 (s[x]) 表示 (x) 子树内的点的 (a[i]) 之和,(sum) 表示 (sum s[son]),(mx) 表示 (max(s[son]))
    那么这个点最多被占领: (min(sum-1,2*(sum-mx),2*(sum-a[x])))
    (dfs) 一遍就可以有 (30) 分了

    现在只需要优化修改操作了
    修改只会影响到这个点到根的路径上的点的贡献
    我们把 (s[son]*2>s[x]) 的儿子作为重儿子,那么就会得到轻重链剖分
    我们按照 (access) 那样暴力改上去,复杂度就和 (access) 是一样的了
    和普通的 (access) 不同的是,虚边不一定会变成实边,但虚边个数是 (log) 的,所以复杂度是对的

    (LCT) 维护每一个点的权值或者维护虚子树都可以做,不过维护虚子树就不需要打 (lazy)

    (30)(.cpp)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=4e5+10;
    int n,Q,a[N],head[N],nxt[N*2],to[N*2],num=0;ll ans=0,s[N],mx[N];
    inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
    inline void dfs(int x,int last){
    	mx[x]=s[x]=a[x];
    	for(int i=head[x];i;i=nxt[i]){
    		int u=to[i];
    		if(u==last)continue;
    		dfs(u,x);
    		mx[x]=max(mx[x],s[u]);
    		s[x]+=s[u];
    	}
    	ans+=min(s[x]-1,2*(s[x]-mx[x]));
    }
    int main(){
    	freopen("pp.in","r",stdin);
    	freopen("pp.ans","w",stdout);
    	scanf("%d%d",&n,&Q);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	int x,y;
    	for(int i=1;i<n;i++){
    		scanf("%d%d",&x,&y);
    		link(x,y);link(y,x);
    	}
    	dfs(1,1);
    	cout<<ans<<endl;
    	while(Q--){
    		scanf("%d%d",&x,&y);
    		a[x]+=y;ans=0;
    		dfs(1,1);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    
    

    (100)(.cpp)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<class T>void gi(T &w){
    	char ch=getchar();w=0;
    	while(ch>'9' || ch<'0')ch=getchar();
    	while(ch>='0' && ch<='9')w=w*10+ch-'0',ch=getchar();
    }
    const int N=4e5+10;
    int n,Q,ch[N][2],head[N],nxt[N*2],num=0,to[N*2];
    int fa[N],k[N];ll ans=0,xu[N],s[N],a[N];
    inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
    inline void dfs(int x,int last){
    	s[x]=a[x];
    	ll mx=a[x];int y=x;
    	for(int i=head[x],u;i;i=nxt[i]){
    		if((u=to[i])==last)continue;
    		fa[u]=x;dfs(u,x);s[x]+=s[u];
    		if(s[u]>mx)mx=s[u],y=u;
    	}
    	xu[x]=s[x];
    	if(mx<<1>s[x]){
    		ans+=(s[x]-mx)<<1;
    		if(x!=y)ch[x][1]=y,xu[x]-=s[y];
    		else k[x]=1;
    	}
    	else ans+=s[x]-1,k[x]=2;
    }
    inline bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    inline void upd(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+xu[x];}
    inline void rotate(int x){
    	int y=fa[x];bool t=ch[y][1]==x;
    	ch[y][t]=ch[x][!t];fa[ch[y][t]]=y;
    	ch[x][!t]=y;fa[x]=fa[y];
    	if(!isrt(y))ch[fa[y]][ch[fa[y]][1]==y]=x;
    	fa[y]=x;upd(y);upd(x);
    }
    inline void splay(int x){
    	while(!isrt(x)){
    		int y=fa[x],p=fa[y];
    		if(isrt(y))rotate(x);
    		else if((ch[p][0]==y)==(ch[y][0]==x))rotate(y),rotate(x);
    		else rotate(x),rotate(x);
    	}
    }
    inline void access(int x,int t){
    	int y=0;ll v=0;
    	while(x){
    		splay(x);
    		v=s[x]-s[ch[x][0]];
    		if(k[x]==2)ans-=v-1;
    		else ans-=(v-(k[x]?a[x]:s[ch[x][1]]))<<1;
    		v+=t;s[x]+=t;xu[x]+=t;a[x]+=(y?0:t);
    		if(s[y]<<1>v)xu[x]+=s[ch[x][1]],xu[x]-=s[ch[x][1]=y];
    
    		if(s[ch[x][1]]<<1>v)ans+=(v-s[ch[x][1]])<<1,k[x]=0;
    		else{
    			if(ch[x][1])xu[x]+=s[ch[x][1]],ch[x][1]=0;
    			if(a[x]<<1>v)ans+=(v-a[x])<<1,k[x]=1;
    			else k[x]=2,ans+=v-1;
    		}
    		x=fa[y=x];
    	}
    	printf("%lld
    ",ans);
    }
    int main(){
      freopen("pp.in","r",stdin);
      freopen("pp.out","w",stdout);
      int x,y;
      gi(n);gi(Q);
      for(int i=1;i<=n;i++)gi(a[i]);
      for(int i=1;i<n;i++){
    	  gi(x);gi(y);
    	  link(x,y);link(y,x);
      }
      dfs(1,1);
      cout<<ans<<endl;
      while(Q--)gi(x),gi(y),access(x,y);
      return 0;
    }
    
    
  • 相关阅读:
    【prufer编码】BZOJ1430 小猴打架
    【费马小定理】BZOJ3260 跳
    【欧拉函数】BZOJ2705: [SDOI2012]Longge的问题
    【卡特兰数】BZOJ1485: [HNOI2009]有趣的数列
    【缩点+拓扑判链】POJ2762 Going from u to v or from v to u?
    【Floyd】BZOJ1491: [NOI2007]社交网络
    【转】对信息学竞赛中调试方法的建议
    【建图+拓扑判环】BZOJ3953: [WF2013]Self-Assembly
    【dfs判负环】BZOJ1489: [HNOI2009]最小圈
    【二分+最小树形图】UVA11865 比赛网络
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8886792.html
Copyright © 2011-2022 走看看