zoukankan      html  css  js  c++  java
  • [国家集训队]种树 贪心 堆

    ~~~题面~~~

    题解:

      其实是一个神奇的贪心。如果m > n / 2,那么显然无解,否则肯定有解。如果没有相邻不能取的限制,那么直接贪心取前m大即可,但是有这个限制。所以考虑怎么维护这个性质。
      首先有一个结论,如果是贪心的取,对于一个点,要么取它旁边的两个,因为是贪心的取的,如果只取了旁边中的一个的话,那还不如就取它自己,而不取旁边的,因为只取旁边的一个肯定比取它小,还不会干扰别的树的取舍。

      那么基于这个结论,可以观察到,如果取x,那么被限制不能取的树是x - 1和x + 1,如果取它两边的数,那就么被限制的就是x, x + 2, x - 2,而x在取x的时候就已经被限制不能取第二次了,也就是从取x转变为取x + 1和x - 1时,多出来的限制只有x - 2, x + 2,所以如果取出x后,就把x - 1, x + 1合并成一个节点,那么x + 2, x - 2就相当于是这个新节点的相邻节点,于是就可以再像取x时一样做了。而因为取了这个新的被融合的节点,我们就需要舍弃x这个节点,因此代价应该是w[x  - 1] + w[x + 1] - w[x]。但被限制的标记应该要打在vis[x - 1]和vis[x + 1]上,因为被融合的新点是放在vis[x]的位置上的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 201000
     5 #define LL long long
     6 int n, m, ans;
     7 int a[AC], last[AC], Next[AC];
     8 bool z[AC];
     9 
    10 struct node{
    11     int w, id;
    12 };
    13 
    14 struct cmp{
    15     bool operator () (node a, node b)
    16     {
    17         return a.w < b.w;
    18     }
    19 };
    20 
    21 priority_queue<node, vector<node>, cmp> q;
    22 
    23 inline int read()
    24 {
    25     int x = 0; char c = getchar(); bool z = false;
    26     while(c > '9' || c < '0') 
    27     {
    28         if(c == '-') z = true;
    29         c = getchar();
    30     }
    31     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    32     if(!z) return x;
    33     else return -x;
    34 }
    35 
    36 void pre()
    37 {
    38     n = read(), m = read();
    39     if(m > n / 2) 
    40     {
    41         printf("Error!
    ");
    42         exit(0);
    43     }
    44     for(R i = 1; i <= n; i ++) a[i] = read();
    45 }
    46 
    47 void build()
    48 {
    49     for(R i = 1; i <= n; i ++) 
    50     {
    51         node x = (node){a[i], i};
    52         last[i] = i - 1, Next[i] = i + 1;
    53         if(!last[i]) last[i] = n;
    54         if(Next[i] == n + 1) Next[i] = 1;
    55         q.push(x);
    56     }
    57 }
    58 
    59 void change(int x)
    60 {
    61     z[x] = true;
    62     Next[last[x]] = Next[x], last[Next[x]] = last[x];
    63     Next[x] = last[x] = 0;//更新新坑周围的坑的前驱和后继
    64 }
    65 
    66 void work()
    67 {
    68     node x;
    69     for(R i = 1; i <= m; i ++)
    70     {
    71         while(z[q.top().id]) q.pop();//因为选了一个点之后,这个点旁边的两个点被标记,于是旁边的就不能选了,但是这个点可以再选一次
    72         x = q.top();
    73         q.pop();
    74         ans += x.w;
    75         int l = last[x.id], r = Next[x.id];//error!!!是x.id不是i啊。。。。
    76         a[x.id] = a[l] + a[r] - a[x.id];//相当于合并了3个坑
    77         node now = (node){a[x.id], x.id};
    78         change(l), change(r);
    79         q.push(now);
    80     }
    81     printf("%d
    ", ans);
    82 }
    83 
    84 int main()
    85 {
    86 //    freopen("in.in", "r", stdin);
    87     pre();
    88     build();
    89     work();
    90 //    fclose(stdin);
    91     return 0;
    92 }
  • 相关阅读:
    From使用post与使用get区别
    HTML 自动跳转代码
    mjpgstreamer译文
    DOCUMENT.GETELEMENTBYID使用
    查看Linux内核版本的命令
    CGI编程学习5 穿插HTML,CSS零星知识
    使用Javascript显示时间
    北京大学<Perl 循序渐进>
    html之marquee详解
    Apache支持ASP.NET方法浅析
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9599259.html
Copyright © 2011-2022 走看看