【ZJOI2018】历史【结论】【LCT思想】
题意:一棵nnn个点的树,每个点有权值aia_iai,初始时给定。维护:
单点权值加上一个正数。
询问每个点恰好执行aia_iai次 access 操作,任意安排顺序的条件下切换轻重链总次数的最大值。
n≤4×105n\leq 4\times 10^5n≤4×105
一道奇怪的题(?)
对于一个点uuu,考虑它子树内两个相邻 access的点,它们在uuu这里切换了一次当且仅当它们属于 不同的 uuu的儿子 为根 的 子树。(为了方便,我们把uuu单独这一个点看成uuu的子树。)
把来自同一个子树内的一次access看成一种颜色的小球,我们希望相邻的不同色小球尽量多。
结论 设总小球数为nnn,颜色最多的小球个数为mmm,那么最大贡献为min{n−1,2(n−m)}\min\{n-1,2(n-m)\}min{n−1,2(n−m)}
证明
考虑把最多的颜色摆出来,在m−1m-1m−1个空隙中放其他颜色。
- 假设最多的颜色只有一种
把所有其他颜色的球依次往空隙中放,放完一个颜色继续往下一个空隙放,空隙放完了再从第一个开始。
如果空隙都放完了,即n−m≥m−1n-m\geq m-1n−m≥m−1,即n−1≤2(n−m)n-1\leq2(n-m)n−1≤2(n−m),所有相邻的小球颜色都不同,答案为n−1n-1n−1
否则颜色最多的球一定会相邻。考虑开始时贡献为000,插入一个任意颜色的球最多让贡献+2+2+2,上面的构造方法可以达到这个最大值,所以最大贡献为2(n−m)2(n-m)2(n−m)
- 最多的颜色有多种
显然这时m≤n2m\leq \frac{n}{2}m≤2n,把最多的颜色各取一个构成一组绑在一起,然后其他颜色用同样的方法放在组之间的空隙内,这样不会有同色相邻。
就可以O(n)O(n)O(n)处理了
然后考虑怎么用数据结构维护这玩意
上面已经推过了,虽然表达式看起来很奇怪,但实际上本质上是判断最大的子树是否超过自己的一半(这里的大小都是指aaa的和)
一点也不自然地想到实链剖分
具体而言,设sumusum_usumu表示uuu子树内权值和,对于uuu和它的儿子vvv,如果2sumv>sumu+12sum_v>sum_u+12sumv>sumu+1(先不管uuu本身什么的),那么vvv是uuu的重儿子,显然重儿子最多有一个。如果不存在这样的儿子就没有重儿子。
这样做的好处是重链上都是取的min{n−1,2(n−m)}\min\{n-1,2(n-m)\}min{n−1,2(n−m)}的右边这项,而把父亲和儿子加上同一个正数时左边会变大,右边不会变,而右边本来就比左边小,不仅仍然是重边,连这个点的答案都不会改变,直接跳过去就可以了。而到根的轻边是O(logV)O(\log V)O(logV)的,直接讨论一下轻边的情况即可。
具体而言就是魔改一下LCT的access,跳轻边的时候需要判一下。具体实现的时候很诡异,建议直接看代码。
注意复杂度是通过轻边条数而非finger-search保证的,所以必须在修改前dfs一遍处理出重儿子
复杂度应该是O(nlognlogV)O(n\log n\log V)O(nlognlogV)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#define MAXN 400005
using namespace std;
typedef long long ll;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int ch[MAXN][2],fa[MAXN];
ll val[MAXN],lz[MAXN];
inline void pushlzy(int x,ll v){val[x]+=v,lz[x]+=v;}
inline void pushdown(int x)
{if (lz[x]){if (ch[x][0]) pushlzy(ch[x][0],lz[x]);if (ch[x][1]) pushlzy(ch[x][1],lz[x]);lz[x]=0;}
}
inline int get(int x){return ch[fa[x]][1]==x;}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void rotate(int x)
{int y=fa[x],z=fa[y];int l=get(x),r=l^1;int w=ch[x][r];if (!isroot(y)) ch[z][get(y)]=x;ch[x][r]=y,ch[y][l]=w;if (w) fa[w]=y;fa[y]=x,fa[x]=z;
}
int q[MAXN],tp;
inline void splay(int x)
{q[tp=1]=x;for (int i=x;!isroot(i);i=fa[i]) q[++tp]=fa[i];for (int i=tp;i>=1;i--) pushdown(q[i]);while (!isroot(x)){int y=fa[x];if (!isroot(y)){if (get(x)==get(y)) rotate(y);else rotate(x);}rotate(x);}
}
ll res[MAXN],a[MAXN],ans;
inline int findrt(int x){while (ch[x][0]) pushdown(x),x=ch[x][0];return x;}
inline void access(int x,int v)
{a[x]+=v;for (int y=0;x;y=x,x=fa[x]){splay(x);val[x]+=v;if (ch[x][0]) pushlzy(ch[x][0],v);ans-=res[x];int t=findrt(ch[x][1]);if (2*(val[x]-val[t])>=val[x]-1) ch[x][1]=0;t=findrt(y);if (2*(val[x]-val[t])<val[x]-1) ch[x][1]=y;t=findrt(ch[x][1]);res[x]=(t? 2*(val[x]-val[t]):val[x]-1);if (2*a[x]>val[x]+1) res[x]=2*(val[x]-a[x]);ans+=res[x];}
}
vector<int> e[MAXN];
void dfs(int u)
{val[u]=a[u];for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]){fa[e[u][i]]=u;dfs(e[u][i]);val[u]+=val[e[u][i]];}for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]&&2*val[e[u][i]]>val[u]+1)ch[u][1]=e[u][i];res[u]=(ch[u][1]? 2*(val[u]-val[ch[u][1]]):val[u]-1);if (2*a[u]>val[u]+1) res[u]=2*(val[u]-a[u]);ans+=res[u];
}
int main()
{int n,m;n=read(),m=read();for (int i=1;i<=n;i++) a[i]=read();for (int i=1;i<n;i++){int u,v;u=read(),v=read();e[u].push_back(v),e[v].push_back(u);}dfs(1); printf("%lld\n",ans);while (m--){int x,v;x=read(),v=read();access(x,v);printf("%lld\n",ans);}return 0;
}
【ZJOI2018】历史【结论】【LCT思想】相关推荐
- 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)
洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...
- BZOJ5212 ZJOI2018历史(LCT)
首先相当于最大化access的轻重边交换次数. 考虑每个点作为战场(而不是每个点所代表的国家与其他国家交战)对答案的贡献,显然每次产生贡献都是该点的子树内(包括自身)此次access的点与上次acce ...
- [LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史
[NOI2014] 魔法森林 题目 按照aaa精灵从小到大排序 按顺序插入每一条边 加入第iii条边后的最小代价为a[i]a[i]a[i]加上从111到nnn的所有路径中最大bbb最小的路径代价 维护 ...
- Luogu4338 ZJOI2018 历史 LCT、贪心
传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...
- [ZJOI2018]历史,洛谷P4338,类LCT维护
正题 题目大意,大致就是给出一棵有根数,给出每个点access的次数,要你安排顺序,求轻重边切换最多多少次,动态加次数. 第一步其实还挺好想的,思考一下如何静态做,发现相当于对于每一个节点,就是让各个 ...
- P4338 [ZJOI2018]历史 LCT+树形DP
\(\color{#0066ff}{ 题目描述 }\) 这个世界有 n 个城市,这 n 个城市被恰好 \(n-1\) 条双向道路联通,即任意两个城市都可以 互相到达.同时城市 1 坐落在世界的中心,占 ...
- [ZJOI2018]历史
Description: 给定一棵树,定义每个点的操作为把这个点到1号点的路径覆盖上颜色i,每次该点到1号点经过的不同颜色段数会加到答案中,要使所有点按某一顺序操作完后答案最大 给定每个点要执行的操作 ...
- 【luogu P4338】【LOJ 2434】历史(LCT)
历史 题目链接:luogu P4338 / LOJ 2434 题目大意 给你一棵树,其中 1 是根,然后给你每个点的 access 次数,要你规划一个 access 的顺序,使得轻重链的切换次数最大. ...
- P4338-[ZJOI2018]历史【LCT】
正题 题目链接:https://www.luogu.com.cn/problem/P4338 题目大意 给出nnn个点的一棵树,和每个点进行accessaccessaccess的次数aia_iai, ...
最新文章
- 判断均匀平面波的极化形式_测瑞通|怎样判断电波暗室的性能?
- 爬虫学习笔记(二十一)—— Appium
- 元气骑士里的超级计算机,元气骑士:本以为“素颜相机”够火,直到看到它,一秒笑出猪叫声...
- 一个需要原创精神的年代
- NYOJ 240 小明的调查统计(二)
- a113 智能音箱芯片方案_高通入局智能音箱,首款四核单芯片方案曝光
- clear ,refresh,free
- 理解程序与指令 人类指令与计算机指令
- 常见c#正则表达式类学习整理
- pytorch torch.utils.data.Dataset
- Network-based Fraud Detection for Social Security Fraud
- MS OFFICE 2019下载及使用
- 数据库 习题答案 系统概念 第七章
- 思科二层冗余技术对比---PortChannel/StackWise/VSS/vPC
- 各种水龙头拆卸图解_各种水龙头拆卸图解
- Oralce性能优化-绑定变量窥视
- GBase 8c产品家族及功能简介
- 递归计算过程与迭代计算过程
- 关于Attention的超详细讲解
- Java小游戏-幸运抽奖-进阶版(可多抽取多次)
热门文章
- 高考成绩接近满分,却被清华北大拒绝,被称“中国最帅科学家”
- 如何用Python进行数据探索,探索竞赛优胜方案?
- 盘点那些让程序员目瞪口呆的Bug都有什么?
- java定义接口_一文知道Java中接口的定义
- 5单个编译总会编译全部_VS2019 v16.5 MSVC编译器后端更新汇总
- vue click事件_vue指令用法
- java中如何运行小程序_一起学java(一)——运行第一个小程序
- oracle clob 查询换行,sqoop clob从Oracle导入到hive 回车换行导致记录增多
- pearson相关系数_Pearson(皮尔逊)相关系数
- python while循环true_Python while循环,pause while not,true时继续?