zoukankan      html  css  js  c++  java
  • 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门

    ZJOI的考场上最弱外省选手T2 10分成功滚粗。。。。。。

    首先要想到30分的结论

    说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点

    想到了又有什么用?反正想不到最大的贡献是怎么推出来的

    然后晚上心中怀着九条CNM看完了Solution.pdf

    貌似对我这个蒟蒻来说也只有这一题可做了。。。。。。

    已知书上每个点access的总次数,构造出一个顺序,最大化虚实边的切换总次数

    其实如果能发现最优顺序的构造是没有后效性的话,问题便可以进一步简化

    考虑每个点的子树。假设已经对所有子树中的点构造出了一个最优顺序(一个序列),那么一定不会和它的所有祖先的子树中的最优序列产生冲突。这个并不好证明,仔细想一想应该能发现。

    于是就可以单独考虑每个点(x)。能对(x)的实子边产生影响的是x的所有子树和(x)本身(access(x)会使(x)没有实子边),每切换一次都会使答案(+1)。显然同一个子树中产生的影响是相同的。于是我们要让来自不同子树(或(x)本身)尽可能交替access。当没有某个子树(或(x)本身)的(a)总和过大时,可以构造出使得(除了第一次)每一次access都有贡献的方案。如果某个子树(或(x)本身)的(a)总和过大,大于所有子树总和的一半时,是不可以的,那个子树(或(x)本身)的某几次操作肯定不会有贡献。用数学公式大概表示每个点的贡献为((S)为子树的(a)之和,(c)(x)的每个子树)

    [min{S_x-1,2*(S_x-max{a_x,forall S_c})} ]

    (max{a_x,forall S_c}gt {S_x+1over2})(min)取后者

    用树形DP算出来就有30分了

    那么怎样快速修改呢?

    首先,对某个点的(a)加上一个值(w),只可能会影响该点到根的路径上的点的贡献。

    因为是加一个值,所以假如这些点中某些点的子树(S)大于它父亲子树(S)的一半,那么(S_x+w)(max{a_x,forall S_c})也会(+w),带入上式发现贡献是不变的!

    看到某个子树S大于所有子树总和的一半,有没有想到树剖?树剖的轻重边就是这样划分的啊!(反正我这种蒟蒻就是想不到

    同样对维护好每个点子树S的树进行轻重链剖分,某些点的子树a之和大于它父亲子树a之和的一半就连重边(否则连轻边),这样每个点至多有一个重儿子。类似树剖的证明,每个点到根的轻边总数(也就是我们可能会修改的点数)是(logsum a)级别的!

    修改的复杂度也有保障啦!我们只要快速找到这些点就好了。用实链剖分维护子树信息即可(应该不能叫LCT吧,没有makeroot,link和cut,对于这个问题蒟蒻的LCT总结对LCT的概念也改了改,欢迎Julao们指正!)。全局保存ans,每次进行类access操作找到虚边,就让ans直接减去以前的贡献,加上w以后判断当前的情况决定是否要切换虚实边并让ans加上新的贡献即可。

    为了方便计算以前的贡献,蒟蒻觉得可以保存一下以前贡献的类型(无非就三种,某子树过大、自己过大、都不是很大)算的时候就省去了一些判断的时间。

    代码细节巨多,尤其是类access更新答案那部分。所以就算考场上想到了一些东西,我这种蒟蒻也未必写得出来吧!疯狂膜拜考场切T2的laofu爷Orzzzzzzzzzzzz!

    #include<cstdio>
    #define RG register
    #define R RG int
    #define I inline void
    #define lc c[x][0]
    #define rc c[x][1]
    #define G ch=getchar()
    typedef long long L;
    const int N=400009,M=N<<1;
    int f[N],c[N][2],he[N],ne[M],to[M];
    L ans,a[N],si[N],s[N];
    short tp[N];
    bool r[N];
    template<typename T>
    I in(RG T&x){
    	RG char G;
    	while(ch<'-')G;
    	x=ch&15;G;
    	while(ch>'-')x*=10,x+=ch&15,G;
    }
    inline bool nroot(R x){
    	return c[f[x]][0]==x||c[f[x]][1]==x;
    }
    I up(R x){
    	s[x]=s[lc]+s[rc]+si[x]+a[x];
    }
    I rot(R x){
    	R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
    	if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;
    	up(f[w]=y);f[y]=x;f[x]=z;
    }
    I splay(R x){
    	R y;
    	while(nroot(x)){
    		if(nroot(y=f[x]))
    			rot((c[f[y]][0]==y)^(c[y][0]==x)?x:y);
    		rot(x);
    	}
    	up(x);
    }
    void dp(R x){//dp预处理答案
    	R y,i,mp=x;
    	RG L mx=a[x];
    	for(i=he[x];i;i=ne[i]){
    		if(f[x]==(y=to[i]))continue;
    		f[y]=x;dp(y);
    		si[x]+=s[y];
    		if(mx<s[y])mx=s[y],mp=y;
    	}
    	if(mx<<1>(s[x]=si[x]+a[x])){
    		ans+=(s[x]-mx)<<1;
    		if(x!=mp)si[x]-=s[rc=mp];//子树过大
    		else tp[x]=1;//自己过大
    	}
    	else tp[x]=2,ans+=s[x]-1;//都不是很大
    }
    int main(){
    	R n,m,i,p=0,x,y;
    	RG L w,S;
    	in(n);in(m);
    	for(i=1;i<=n;++i)in(a[i]);
    	for(i=1;i<n;++i){
    		in(x);in(y);
    		to[++p]=y;ne[p]=he[x];he[x]=p;
    		to[++p]=x;ne[p]=he[y];he[y]=p;
    	}
    	dp(1);printf("%lld
    ",ans);
    	while(m--){
    		in(x);in(w);
    		for(y=0;x;x=f[y=x]){
    			splay(x);
    			S=s[x]-s[lc];//算原来子树a总和,注意减s[lc]
    			ans-=tp[x]<2?(S-(tp[x]?a[x]:s[rc]))<<1:S-1;
    			S+=w;s[x]+=w;(y?si:a)[x]+=w;
    			if(s[y]<<1>S)si[x]+=s[rc],si[x]-=s[rc=y];//虚实切换
    			if(s[rc]<<1>S)   tp[x]=0,ans+=(S-s[rc])<<1;//子树过大
    			else{
    				if(rc)si[x]+=s[rc],rc=0;//没有子树过大,一定变虚
    				if(a[x]<<1>S)tp[x]=1,ans+=(S-a[x])<<1;//自己过大
    				else         tp[x]=2,ans+=S-1,rc=0;//都不是很大
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    RMAN 高级恢复
    从问题域出发认识Hadoop生态系统
    下一代 Hadoop YARN :相比于MRv1,YARN的优势
    Sensei:分布式, 实时, 半结构化数据库
    盘点2012:云计算的春天
    Apache Tajo:一个运行在YARN上支持SQL的分布式数据仓库
    实现多个jetty实例开机后自动启动
    淘宝在数据处理领域的项目及开源产品介绍
    SenseiDB架构设计分析
    在Hadoop上运行SQL:程序员需知晓的13种数据工具
  • 原文地址:https://www.cnblogs.com/flashhu/p/8709637.html
Copyright © 2011-2022 走看看