zoukankan      html  css  js  c++  java
  • Luogu4338 ZJOI2018 历史 LCT、贪心

    传送门

    题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access$次数。$N leq 4 imes 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 imes (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 ext{次数})$级别的,复杂度也符合要求。所以可以使用$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
    " , sum);
    142     while(M--){
    143         int a = read() , x = read();
    144         access(a + N , x);
    145         printf("%lld
    " , sum);
    146     }
    147     return 0;
    148 }
  • 相关阅读:
    快速排序
    将指定目录下的所有子文件或子目录加载到TreeView
    导入英汉文本,用字符串切割,泛型集合存储的英汉字典
    取年月日的字符串方法
    简化的MVC-导入模板HTML,导入数据txt,用字符串方法生成JS菜单
    索引器的使用
    打开文件练习
    泛型委托
    将正则表达式转化成确定的有限自动机
    青蛙过桥
  • 原文地址:https://www.cnblogs.com/Itst/p/10046383.html
Copyright © 2011-2022 走看看