1. 程式人生 > >[國家集訓隊]種樹 貪心 堆

[國家集訓隊]種樹 貪心 堆

inline chan ret als 國家 。。 return cmp work

~~~題面~~~

題解:

  其實是一個神奇的貪心。如果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!\n"); 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\n", 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 }

[國家集訓隊]種樹 貪心 堆