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;
    }
    
    
  • 相关阅读:
    ASP.NET MVC 重点教程一周年版 第二回 UrlRouting
    ASP.NET MVC 重点教程一周年版 第三回 Controller与View
    DynamicData for Asp.net Mvc留言本实例 下篇 更新
    Asp.net MVC视频教程 18 单选与复选框
    使用ASP.NET MVC Futures 中的异步Action
    ASP.NET MVC RC 升级要注意的几点
    ATL、MFC、WTL CString 的今生前世
    msvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义
    关于Windows内存的一些参考文章
    Windows访问令牌相关使用方法
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8886792.html
Copyright © 2011-2022 走看看