传送门

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


ZJOI2018真的都是大火题

首先一个小小的转化:对于每个非叶子节点,新开一个叶子节点,将当前非叶子节点的$access$次数转移到这些叶子节点上,这样所有的$access$操作都在叶子节点进行,可以少很多的判断。

接着我们需要考虑在每一个点上最大化方案总数。因为必须相邻两次$access$由不同子树的叶子节点发起才能够贡献$1$的答案,而我们希望每一次$access$都能尽可能多贡献答案,所以尽可能让相邻两次$access$来自不同子树。考虑树上某一个非叶子节点$x$,其所有儿子为集合$i$,$S$表示子树$access$次数总和,显然答案贡献的最大值与$\sum S_i$与$max\{S_i\}$相关,因为当$max\{S_i\}$占$\sum S_i$比例特别大的时候,则必定要有很多同一子树来的$access$操作被放在一起。

现讲结论吧,最大$access$贡献是

$$min\{S_i-1 , 2 \times (\sum S_i - max \{S_i\})\}$$

也就是在$max\{S_i\} > \frac{\sum S_i + 1}{2}$时总贡献数量会取右边一项

$min$中的左边一项表示的是任意两个$access$之间都产生$1$的贡献(最优的情况),而对于右边的项,因为只有取到最大值的子树的贡献次数变少了,那么我们考虑所有其他子树,它们每一次$access$都可以在这一次$access$的之前、之后的$access$中取到$2$的贡献,所以贡献总和就是右边那一项,树形$DP$计算一次就能获得$30pts$。

接着我们考虑修改操作。如果在某一个点的子树集合$i$上存在一个子树$x$满足$S_x > \frac{\sum S_i + 1}{2}$,那么如果我们在子树$x$上加上$access$次数$w$,和和最大值同时增加了$w$,也就是说贡献没有变化。我们考虑将满足$S_x > \frac{\sum S_i + 1}{2}$的点与其父亲连一条实边,表示这一条边连接的两个点之间无需转移,而其他的边就连为轻边。

观察一下条件:$S_x > \frac{\sum S_i + 1}{2}$,是不是很像重链剖分?其实实质就是重链剖分

然后我们就只需要考虑轻边上的转移了。可以知道每一个点到根的轻边的数量是$log \, \sum (access \text{次数})$级别的,复杂度也符合要求。所以可以使用$LCT$动态维护轻重边的划分,外部计算全局答案,每一次找到一条轻边的时候,看能否将其改为重边,去掉以前这个点的贡献,加上当前的贡献即可。

  1 #include<bits/stdc++.h>
  2 #define int long long
  3 #define lch Tree[x].ch[0]
  4 #define rch Tree[x].ch[1]
  5 #define mid ((Tree[x].sum + 1) >> 1)
  6 //This code is written by Itst
  7 using namespace std;
  8
  9 inline int read(){
 10     int a = 0;
 11     bool f = 0;
 12     char c = getchar();
 13     while(c != EOF && !isdigit(c)){
 14         if(c == '-')
 15             f = 1;
 16         c = getchar();
 17     }
 18     while(c != EOF && isdigit(c)){
 19         a = (a << 3) + (a << 1) + (c ^ '0');
 20         c = getchar();
 21     }
 22     return f ? -a : a;
 23 }
 24
 25 const int MAXN = 400010;
 26 struct node{
 27     int fa , ch[2] , sum , non_sum;
 28     bool type;
 29 }Tree[MAXN << 1];
 30 struct Edge{
 31     int end , upEd;
 32 }Ed[MAXN << 2];
 33 int a[MAXN] , head[MAXN] , sum , N , cntEd;
 34
 35 inline bool nroot(int x){
 36     return Tree[Tree[x].fa].ch[0] == x || Tree[Tree[x].fa].ch[1] == x;
 37 }
 38
 39 inline bool son(int x){
 40     return Tree[Tree[x].fa].ch[1] == x;
 41 }
 42
 43 inline void pushup(int x){
 44     Tree[x].sum = Tree[lch].sum + Tree[rch].sum + Tree[x].non_sum + (x > N ? a[x - N] : 0);
 45 }
 46
 47 inline void rotate(int x){
 48     bool f = son(x);
 49     int y = Tree[x].fa , z = Tree[y].fa , w = Tree[x].ch[f ^ 1];
 50     if(nroot(y))
 51         Tree[z].ch[son(y)] = x;
 52     Tree[x].fa = z;
 53     Tree[x].ch[f ^ 1] = y;
 54     Tree[y].fa = x;
 55     Tree[y].ch[f] = w;
 56     if(w)
 57         Tree[w].fa = y;
 58     pushup(y);
 59 }
 60
 61 inline void Splay(int x){
 62     while(nroot(x)){
 63         if(nroot(Tree[x].fa))
 64             rotate(son(x) == son(Tree[x].fa) ? Tree[x].fa : x);
 65         rotate(x);
 66     }
 67     pushup(x);
 68 }
 69
 70 inline void access(int x , int w){
 71     a[x - N] += w;
 72     Splay(x);
 73     while(Tree[x].fa){
 74         Splay(Tree[x].fa);
 75         int k = Tree[x].fa , t = Tree[k].sum - Tree[Tree[k].ch[0]].sum;
 76         if(Tree[k].ch[1])
 77             sum -= (t - Tree[Tree[k].ch[1]].sum) << 1;
 78         else
 79             sum -= t - 1;
 80         Tree[k].non_sum += w;
 81         Tree[k].sum += w;
 82         t += w;
 83         if(Tree[Tree[k].ch[1]].sum < Tree[x].sum){
 84             Tree[k].non_sum = Tree[k].non_sum - Tree[x].sum + Tree[Tree[k].ch[1]].sum;
 85             Tree[k].ch[1] = x;
 86         }
 87         if(((t + 1) >> 1) < Tree[Tree[k].ch[1]].sum)
 88             sum += (t - Tree[Tree[k].ch[1]].sum) << 1;
 89         else{
 90             sum += t - 1;
 91             Tree[k].non_sum += Tree[Tree[k].ch[1]].sum;
 92             Tree[k].ch[1] = 0;
 93         }
 94         x = k;
 95     }
 96 }
 97
 98 inline void addEd(int a , int b){
 99     Ed[++cntEd].end = b;
100     Ed[cntEd].upEd = head[a];
101     head[a] = cntEd;
102 }
103
104 void dfs(int x , int fa){
105     Tree[x].fa = fa;
106     if(x > N)
107         return;
108     for(int i = head[x] ; i ; i = Ed[i].upEd)
109         if(Ed[i].end != fa){
110             dfs(Ed[i].end , x);
111             Tree[x].sum += Tree[Ed[i].end].sum;
112         }
113     for(int i = head[x] ; i ; i = Ed[i].upEd)
114         if(Ed[i].end != fa && mid < Tree[Ed[i].end].sum){
115             sum += (Tree[x].sum - Tree[Ed[i].end].sum) << 1;
116             Tree[x].non_sum = Tree[x].sum - Tree[Ed[i].end].sum;
117             Tree[x].ch[1] = Ed[i].end;
118             return;
119         }
120     sum += Tree[x].sum - 1;
121     Tree[x].non_sum = Tree[x].sum;
122 }
123
124 signed main(){
125 #ifndef ONLINE_JUDGE
126     freopen("4338.in" , "r" , stdin);
127     //freopen("4338.out" , "w" , stdout);
128 #endif
129     N = read();
130     int M = read();
131     for(int i = 1 ; i <= N ; ++i)
132         Tree[i + N].sum = a[i] = read();
133     for(int i = 1 ; i < N ; ++i){
134         int a = read() , b = read();
135         addEd(a , b);
136         addEd(b , a);
137     }
138     for(int i = 1 ; i <= N ; ++i)
139         addEd(i , i + N);
140     dfs(1 , 0);
141     printf("%lld\n" , sum);
142     while(M--){
143         int a = read() , x = read();
144         access(a + N , x);
145         printf("%lld\n" , sum);
146     }
147     return 0;
148 }

转载于:https://www.cnblogs.com/Itst/p/10046383.html

Luogu4338 ZJOI2018 历史 LCT、贪心相关推荐

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

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

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

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

  3. BZOJ5212 ZJOI2018历史(LCT)

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

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

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

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

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

  6. [ZJOI2018]历史

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

  7. P4338 [ZJOI2018]历史(树剖)(暴力)

    前言 有点懊恼的一个题- 并没有其他那些ZJOI那么毒瘤,看出了关键结论,但最后维护卡在log条虚边的伞兵性质上了. 解析 第一眼:感觉根本不可做啊. 冷静一下,既然它还变态的带修,一定是可以转化成比 ...

  8. ZJOI2019一轮停课刷题记录

    Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...

  9. Link-Cut Tree

    Link-Cut Tree 概述. LCT是一种支持动态维护树上路径信息的数据结构,其本质是实链剖分,通过其他数据结构维护实链的信息达到维护路径及一些子树信息的效果(通常为splay) 刚开始学的时候 ...

最新文章

  1. 拼接召回在飞猪交通域的实践
  2. SAP RETAIL 通过自动补货功能触发的采购申请有些啥特殊的地方?
  3. CSharpGL(42)借助帧缓存实现渲染到纹理(RenderToTexture)
  4. 两个实用的Python的装饰器
  5. koa --- [MVC实现之一]自定义路由读取规则
  6. 人民币小写金额转大写金额
  7. ImportError: libpq.so.5: cannot open shared object file: No such file or directory
  8. 2017年4月21号课堂笔记
  9. 不能访问win7计算机,局域网win7无法访问win10,win7访问不了局域网其他电脑
  10. PyTorch 入坑五 autograd与逻辑回归
  11. 基于OpenCV全景图像拼接
  12. C语言pid算法模糊控制,温度PID模糊控制的算法完整
  13. 读书笔记(10)网络规划与设计
  14. 手机测试兼容性的工具
  15. 慕课软件工程(第五章.初始模块结构图的设计)
  16. 六一儿童节(python)
  17. LeetCode | 347. Top K Frequent Elements
  18. python基本函数的使用_python基础之函数的应用
  19. l7sa008b故障代码_美国凯利冷机故障码表
  20. OpenMV感光元件参数设置

热门文章

  1. Android studio 提示‘adb‘ 不是内部或外部命令,也不是可运行的程序的解决方法
  2. 在windows10电脑上录屏难不难?
  3. swift和python哪个好_单从语法角度讲,Swift 完爆 Java Python 和 C#吗?
  4. 职业扫盲:好薪酬制度的两个特征
  5. 热敏电阻NTC103、PT100温度计算公式
  6. 推方块java代码_我的世界无限推方块 | 手游网游页游攻略大全
  7. 台湾地区信用卡客户贷款违约预测
  8. 软件机器人有哪些厂商?国内厂商实力如何?
  9. Acrobat Pro DC 教程:如何将文件合并为 PDF?
  10. Windows环境下的ELK——logstash输出apche日志到控制台(2)