• [BZOJ5212][ZJOI2018]历史


    bzoj
    luogu
    uoj

    题意

    有一棵(LCT),给出每个点的(access)次数,求一个最优的(access)操作顺序使得重轻链切换的次数最多。

    sol

    像我这种外省酱油选手在考场上当然只有10分暴力分
    有没有人像我一样在考场上想到[SDOI2017]树点涂色然而依然没有一点思路的?
    emmmmmm...

    分别考虑每个点的贡献。

    对于一个点来说,如果我们希望这个点的贡献尽量大,那么就需要尽量让每棵子树交替进行(access)。这个时候需要讨论一下:(记点(i)(access)次数为(a_i),点(i)子树中的(a_i)之和为(size_i)
    1、如果(2*a_i>size_i)则说明子树中的(access)次数不能完全和(i)点交替,此时(i)点的贡献为(2*(size_i-a_i))
    2、如果存在(v)(i)的儿子满足(2*size_v>size_i)(显然这样的(v)至多只有一个),那么说明子树(v)以外的点的(access)次数不能完全和子树(v)匹配,此时(i)点的贡献为(2*(size_i-size_v))
    3、若以上两者均不满足,则说明子树(i)中的(access)次数可以完全匹配,此时(i)点的贡献为(size_i-1)

    在上述2、中提到了对于每个(i)而言,满足(2*size_v>size_i)(v)至多只有一个。类比于(LCT)中每个点的重儿子至多只有一个,我们把那个满足条件的(v)当做点(i)的重儿子用(LCT)维护即可。
    每次修改一个点的(a_i)相当于要修改整条路径上的(size_i),可以类似(LCT)中的(access)的写法,对于每一次跳到的(x)做一次左儿子(也就是上方路径)的路径加,以及讨论一下(x)的重儿子的变换情况。

    复杂度不会证。据说是(O(nlog{n}))

    code

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 4e5+5;
    int n,m,to[N<<1],nxt[N<<1],head[N],cnt,fa[N],ch[2][N],son[N];
    ll a[N],size[N],tag[N],f[N],ans;
    void link(int u,int v)
    {
    	to[++cnt]=v;nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    void update(int u)
    {
    	ans-=f[u];f[u]=size[u]-1;
    	if (son[u]) f[u]=(size[u]-size[son[u]])*2;
    	if (a[u]*2>size[u]) f[u]=(size[u]-a[u])*2;
    	ans+=f[u];
    }
    void dfs(int u,int ff)
    {
    	size[u]=a[u];fa[u]=ff;
    	for (int e=head[u];e;e=nxt[e])
    		if (to[e]!=ff)
    			dfs(to[e],u),size[u]+=size[to[e]];
    	for (int e=head[u];e;e=nxt[e])
    		if (to[e]!=ff)
    			if (size[to[e]]*2>size[u]) son[u]=ch[1][u]=to[e];
    	update(u);
    }
    bool Son(int x)
    {
    	return ch[1][fa[x]]==x;
    }
    bool isroot(int x)
    {
    	return ch[0][fa[x]]!=x&&ch[1][fa[x]]!=x;
    }
    void cover(int x,ll v)
    {
    	if (!x) return;
    	size[x]+=v;tag[x]+=v;
    }
    void pushdown(int x)
    {
    	if (!tag[x]) return;
    	cover(ch[0][x],tag[x]);cover(ch[1][x],tag[x]);
    	tag[x]=0;
    }
    void alldown(int x)
    {
    	if (!isroot(x)) alldown(fa[x]);
    	pushdown(x);
    }
    void rotate(int x)
    {
    	int y=fa[x],z=fa[y],c=Son(x);
    	ch[c][y]=ch[c^1][x];if (ch[c][y]) fa[ch[c][y]]=y;
    	fa[x]=z;if (!isroot(y)) ch[Son(y)][z]=x;
    	ch[c^1][x]=y;fa[y]=x;
    }
    void splay(int x)
    {
    	alldown(x);
    	for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
    		if (!isroot(y)) Son(x)^Son(y)?rotate(x):rotate(y);
    }
    int findroot(int x)
    {
    	while (ch[0][x]) pushdown(x),x=ch[0][x];
    	return x;
    }
    void access(int x,int v)
    {
    	a[x]+=v;
    	for (int y=0;x;y=x,x=fa[x])
    	{
    		splay(x);size[x]+=v;cover(ch[0][x],v);
    		if (son[x])
    		{
    			alldown(son[x]);
    			if (size[son[x]]*2<=size[x]) son[x]=ch[1][x]=0;
    		}
    		int gg=findroot(y);
    		if (size[gg]*2>size[x]) son[x]=gg,ch[1][x]=y;
    		update(x);
    	}
    }
    int main()
    {
    	n=gi();m=gi();
    	for (int i=1;i<=n;++i) a[i]=gi();
    	for (int i=1;i<n;++i)
    	{
    		int u=gi(),v=gi();
    		link(u,v);link(v,u);
    	}
    	dfs(1,0);printf("%lld
    ",ans);
    	while (m--)
    	{
    		int x=gi(),y=gi();
    		access(x,y);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    高并发核心技术
    2年java,蚂蚁一面,卒
    Spring Boot 实战 入门
    spring-boot-plus后台快速开发脚手架之代码生成器使用
    spring-boot-plus后台快速开发框架1.0.0.RELEASE发布了
    spring-boot-plus后台快速开发框架1.0.0.RELEASE发布了
    Spring Boot项目使用maven-assembly-plugin根据不同环境打包成tar.gz或者zip
    从尾到头打印单向链表
    单向链表操作
    合并两个排序的数组
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8672671.html
Copyright © 2020-2023  润新知