【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)

题面

洛谷

BZOJ

题解

显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数。

先考虑不带修改的答案。

如果直接考虑全局的答案会很麻烦。

考虑每一个在每一个点处被切换的次数。

显然这个子树之和其子树内的点的\(access\)次数相关,和子树外的点无关。

而在这个点处被切换只有它的子树中不在同一棵子树内的两个点先后进行\(access\)。

对于一个点统计其不同子树内的\(access\)次数和,那么对于当前点而言,等价于有子树个数种颜色,每种颜色可以使用若干次,求一个放置方案数,使得相邻颜色不同的组数最多。

这个东西貌似是场\(agc\)的\(a\)题????

假设颜色总和是\(s\),最大值为\(p\),那么答案就是\(min\{s-1,2*(s-p)\}\)

原因大概就是看看出现次数最多的那个多不多,如果不是很多的话就可以穿插的放。(强行解释.jpg)

那么这样子可以进行一次\(dfs\),\(O(n)\)的算出答案。

然后现在存在了修改操作。

首先发现这个东西是一个加法操作,然后发现会影响的只有到达根的这一段路径。

那么考虑一下\(s-1<2*(s-p)\)推出来是\(2p<s+1\)。

而这样一次操作之后,路径上的\(s\)会做加法,而\(p\)呢????

来一点骚操作,我们对于\(2s_v\le s_u+1\)的、存在父子关系的两个点\(u,v\)之间连上一条轻边,在\(2s_v>s_u+1\)的\(u,v\)之间连上一条重边。

然后我们似乎就完成了一次重链剖分,可以证明每个点只会拥有不超过一个重儿子。

那么也可以发现到根节点的轻边数量是\(log\)级别的。

继续看看,发现进行区间加法的时候,重儿子显然还是满足上述条件,直接跳过不需要考虑。

那么对于一个轻儿子特殊的考虑一下是否需要修改其状态。

那么动态的修改轻重儿子就是\(LCT\)可以完成的事情啦。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 400400
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,m,hson[MAX];
ll a[MAX],s[MAX],ss[MAX],ans;
struct Node{int ch[2],ff;}t[MAX];
bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
void pushup(int x){s[x]=s[t[x].ch[0]]+s[t[x].ch[1]]+ss[x]+a[x];}
void rotate(int x)
{
int y=t[x].ff,z=t[y].ff;
int k=t[y].ch[1]==x;
if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
t[x].ch[k^1]=y;t[y].ff=x;
pushup(y);pushup(x);
}
void Splay(int x)
{
while(!isroot(x))
{
int y=t[x].ff,z=t[y].ff;
if(!isroot(y))
(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}
}
void dfs(int u,int ff)
{
ll mx=a[u];int ms=u;s[u]=a[u];t[u].ff=ff;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
{
dfs(e[i].v,u),s[u]+=s[e[i].v];
if(s[e[i].v]>mx)mx=s[e[i].v],ms=e[i].v; }
ss[u]=s[u]-a[u];
if(2*mx>s[u])
{
ans+=(s[u]-mx)*2;
if(ms==u)hson[u]=0;
else hson[u]=1,t[u].ch[1]=ms,ss[u]-=mx;
}
else ans+=s[u]-1,hson[u]=2;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
dfs(1,0);printf("%lld\n",ans);
while(m--)
{
int x=read(),w=read();
for(int y=0;x;y=x,x=t[x].ff)
{
Splay(x);
ll S=s[x]-s[t[x].ch[0]];
if(hson[x]==0)ans-=(S-a[x])*2;
if(hson[x]==1)ans-=(S-s[t[x].ch[1]])*2;
if(hson[x]==2)ans-=S-1;
S+=w;s[x]+=w;if(y)ss[x]+=w;else a[x]+=w;
if(s[y]*2>S)ss[x]+=s[t[x].ch[1]],ss[x]-=s[y],t[x].ch[1]=y;
if(s[t[x].ch[1]]*2>S)hson[x]=1,ans+=(S-s[t[x].ch[1]])*2;
else
{
if(t[x].ch[1])ss[x]+=s[t[x].ch[1]],t[x].ch[1]=0;
if(a[x]*2>S)hson[x]=0,ans+=(S-a[x])*2;
else hson[x]=2,ans+=S-1,t[x].ch[1]=0;
}
}
printf("%lld\n",ans);
}
return 0;
}

【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)的更多相关文章

  1. link cut tree 入门

    鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...

  2. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  3. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  4. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  5. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  6. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

  7. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  8. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  9. [CodeForces - 614A] A - Link/Cut Tree

    A - Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, ...

随机推荐

  1. yum 命令

    yum( Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器. 基於RPM包管理,能够从指定的服务器自动下载RPM包 ...

  2. Linux kernel support docker storage driver aufs

    How to make docker use aufs in CentOS 7? - Server Faulthttps://serverfault.com/questions/650208/how- ...

  3. git repository description

    Git - Plumbing and Porcelainhttps://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain gith ...

  4. MySQL数据库性能优化思路与解决方法(二转)

    原文:http://bbs.landingbj.com/t-0-242512-1.html 1.锁定表 尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是 ...

  5. 【Python3练习题 013】 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字

    a=input('输入数字>>>') count=int(input('几个数字相加>>>')) ret=[] for i in range(1,count+1): ...

  6. css3特殊图形(气泡)

    一.气泡 效果: body{ background: #dd5e9d; height: 100%; } .paopao { position: absolute; width: 200px; heig ...

  7. 动态SQL3

    Oracle的批量操作 Oracle不支持VALUES(),(),()这种方式,所以不能用上一节所讲的方法. 有时候业务会包含很多次数据库操作,为了减少数据库连接,我们会选择一次提交大量sql, 这时 ...

  8. 剑指offer(8)

    题目: 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路: 第一反应想到的是把数右移,每一位与1相与,然后判断个数,但是若输入的为负数,会出现死循环现象. 所以我们设置一个标志量 ...

  9. SQL Server2012中时间字段为DateTime和VarChar的区别

    在设计数据库的时候varchar类型是一个非常常见的类型,很多字段都可以使用这个类型,所以有时候在设计数据库的时候就很容易习惯性设计该类型,比如说时间类型,我们既可以DateTime类型,又可以使用v ...

  10. python 字符串常用操作方法

    python 字符串常用操作方法 python 字符串操作常用操作,如字符串的替换.删除.截取.赋值.连接.比较.查找.分割等 1.去除空格 str.strip():删除字符串两边的指定字符,括号的写 ...