题意:一棵nnn个点的树,每个点有权值aia_iai​,初始时给定。维护:

  1. 单点权值加上一个正数。

  2. 询问每个点恰好执行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个空隙中放其他颜色。

  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)

  1. 最多的颜色有多种

显然这时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(log⁡V)O(\log V)O(logV)的,直接讨论一下轻边的情况即可。

具体而言就是魔改一下LCT的access,跳轻边的时候需要判一下。具体实现的时候很诡异,建议直接看代码。

注意复杂度是通过轻边条数而非finger-search保证的,所以必须在修改前dfs一遍处理出重儿子

复杂度应该是O(nlog⁡nlog⁡V)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思想】相关推荐

  1. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  2. BZOJ5212 ZJOI2018历史(LCT)

    首先相当于最大化access的轻重边交换次数. 考虑每个点作为战场(而不是每个点所代表的国家与其他国家交战)对答案的贡献,显然每次产生贡献都是该点的子树内(包括自身)此次access的点与上次acce ...

  3. [LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史

    [NOI2014] 魔法森林 题目 按照aaa精灵从小到大排序 按顺序插入每一条边 加入第iii条边后的最小代价为a[i]a[i]a[i]加上从111到nnn的所有路径中最大bbb最小的路径代价 维护 ...

  4. Luogu4338 ZJOI2018 历史 LCT、贪心

    传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...

  5. [ZJOI2018]历史,洛谷P4338,类LCT维护

    正题 题目大意,大致就是给出一棵有根数,给出每个点access的次数,要你安排顺序,求轻重边切换最多多少次,动态加次数. 第一步其实还挺好想的,思考一下如何静态做,发现相当于对于每一个节点,就是让各个 ...

  6. P4338 [ZJOI2018]历史 LCT+树形DP

    \(\color{#0066ff}{ 题目描述 }\) 这个世界有 n 个城市,这 n 个城市被恰好 \(n-1\) 条双向道路联通,即任意两个城市都可以 互相到达.同时城市 1 坐落在世界的中心,占 ...

  7. [ZJOI2018]历史

    Description: 给定一棵树,定义每个点的操作为把这个点到1号点的路径覆盖上颜色i,每次该点到1号点经过的不同颜色段数会加到答案中,要使所有点按某一顺序操作完后答案最大 给定每个点要执行的操作 ...

  8. 【luogu P4338】【LOJ 2434】历史(LCT)

    历史 题目链接:luogu P4338 / LOJ 2434 题目大意 给你一棵树,其中 1 是根,然后给你每个点的 access 次数,要你规划一个 access 的顺序,使得轻重链的切换次数最大. ...

  9. P4338-[ZJOI2018]历史【LCT】

    正题 题目链接:https://www.luogu.com.cn/problem/P4338 题目大意 给出nnn个点的一棵树,和每个点进行accessaccessaccess的次数aia_iai​, ...

最新文章

  1. 判断均匀平面波的极化形式_测瑞通|怎样判断电波暗室的性能?
  2. 爬虫学习笔记(二十一)—— Appium
  3. 元气骑士里的超级计算机,元气骑士:本以为“素颜相机”够火,直到看到它,一秒笑出猪叫声...
  4. 一个需要原创精神的年代
  5. NYOJ 240 小明的调查统计(二)
  6. a113 智能音箱芯片方案_高通入局智能音箱,首款四核单芯片方案曝光
  7. clear ,refresh,free
  8. 理解程序与指令 人类指令与计算机指令
  9. 常见c#正则表达式类学习整理
  10. pytorch torch.utils.data.Dataset
  11. Network-based Fraud Detection for Social Security Fraud
  12. MS OFFICE 2019下载及使用
  13. 数据库 习题答案 系统概念 第七章
  14. 思科二层冗余技术对比---PortChannel/StackWise/VSS/vPC
  15. 各种水龙头拆卸图解_各种水龙头拆卸图解
  16. Oralce性能优化-绑定变量窥视
  17. GBase 8c产品家族及功能简介
  18. 递归计算过程与迭代计算过程
  19. 关于Attention的超详细讲解
  20. Java小游戏-幸运抽奖-进阶版(可多抽取多次)

热门文章

  1. 高考成绩接近满分,却被清华北大拒绝,被称“中国最帅科学家”
  2. 如何用Python进行数据探索,探索竞赛优胜方案?
  3. 盘点那些让程序员目瞪口呆的Bug都有什么?
  4. java定义接口_一文知道Java中接口的定义
  5. 5单个编译总会编译全部_VS2019 v16.5 MSVC编译器后端更新汇总
  6. vue click事件_vue指令用法
  7. java中如何运行小程序_一起学java(一)——运行第一个小程序
  8. oracle clob 查询换行,sqoop clob从Oracle导入到hive   回车换行导致记录增多
  9. pearson相关系数_Pearson(皮尔逊)相关系数
  10. python while循环true_Python while循环,pause while not,true时继续?