1. 程式人生 > >[bzoj4391] [Usaco2015 dec]High Card Low Card 貪心 線段樹

[bzoj4391] [Usaco2015 dec]High Card Low Card 貪心 線段樹

~~~題面~~~

題解:

  觀察到以決策點為分界線,以點數大的贏為比較方式的遊戲都是它的字首,反之以點數小的贏為比較方式的都是它的字尾,也就是答案是由兩段答案拼湊起來的。

  如果不考慮判斷勝負的條件的變化,則有一個比較容易發現的貪心:

  設f[i]為從1開始到i位, 比較方式為點數大的獲勝,最多能贏幾局。

  那麼為了使答案儘可能優,每次我們都會在剩餘牌中找到點數大於對方的 最小的牌,然後出掉。

  同理,設g[i]為從n開始到i位,比較方式為點數小的獲勝,最多能贏幾局,

  則每次都在剩餘牌中選擇點數小於對方的,最大的牌出掉,這樣可以使得答案儘可能優。

  最後的答案則是max(f[i] + g[i + 1]);

  那麼為什麼這樣一定就是合法的呢?

    首先最優性應該是可以理解的,那麼唯一會導致不合法的情況就是至少一張牌a,在兩邊的決策中都出現了(即被出掉了2次)。對於這種情況,任意一張a出掉了2次,因為遊戲次數=牌數,所以必然還對應著一張b沒有被出過。那麼如果b > a,則用b來代替f[i]中的a一定合法,因為f[i]是點數大的獲勝。反之,b < a, 則用b來代替g[i + 1]中的a一定合法,因為g[i]是點數小的獲勝。

  於是為了求出這2個數組,我們需要一個可以支援查詢前驅後繼和刪除的資料結構。

  你可以選擇set,splay,線段樹等等。

  這裡我因為不會用set,懶得寫splay,所以選擇了值域線段樹。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 100100
  5 #define ac 1001000
  6 #define inf INT_MAX
  7 
  8 int n, w, go, ans;
  9 int tree[ac], maxn[ac], minn[ac], l[ac], r[ac];
 10 int s[AC], f[AC], g[AC];
 11 bool z[AC];//記錄哪些牌在對方手裡
12 13 inline int read() 14 { 15 int x = 0;char c = getchar(); 16 while(c > '9' || c < '0') c = getchar(); 17 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 18 return x; 19 } 20 21 inline void upmax(int &a, int b) 22 { 23 if(b > a) a = b; 24 } 25 26 void pre() 27 { 28 n = read(); 29 for(R i = 1; i <= n; i ++) s[i] = read(), z[s[i]] = true; 30 } 31 32 inline void update(int x) 33 { 34 int ll = x * 2, rr = ll + 1; 35 tree[x] = tree[ll] + tree[rr]; 36 maxn[x] = max(maxn[ll], maxn[rr]); 37 minn[x] = min(minn[ll], minn[rr]); 38 } 39 40 void build(int x, int ll, int rr) 41 { 42 l[x] = ll, r[x] = rr; 43 if(ll == rr) 44 { 45 if(!z[ll]) 46 tree[x] = 1, maxn[x] = ll, minn[x] = ll; 47 else minn[x] = inf; 48 return ; 49 } 50 int mid = (ll + rr) >> 1; 51 build(x * 2, ll, mid); 52 build(x * 2 + 1, mid + 1, rr); 53 update(x); 54 } 55 56 void last(int x)//前驅 57 { 58 if(minn[x] > w) return ; 59 if(l[x] == r[x]) 60 { 61 tree[x] = maxn[x] = 0, minn[x] = inf, go = 1; 62 return ; 63 } 64 if(minn[x * 2 + 1] < w) last(x * 2 + 1); 65 else last(x * 2); 66 update(x); 67 } 68 69 void Next(int x)//後繼 70 { 71 if(maxn[x] < w) return ; 72 if(l[x] == r[x]) 73 { 74 tree[x] = maxn[x] = 0, minn[x] = inf, go = 1; 75 return ; 76 } 77 if(maxn[x * 2] > w) Next(x * 2); 78 else Next(x * 2 + 1); 79 update(x); 80 } 81 82 void work() 83 { 84 build(1, 1, 2 * n); 85 for(R i = 1; i <= n; i ++) 86 { 87 w = s[i], go = 0, Next(1); 88 f[i] = f[i - 1] + go; 89 } 90 build(1, 1, 2 * n); 91 for(R i = n; i; i --) 92 { 93 w = s[i], go = 0, last(1); 94 g[i] = g[i + 1] + go; 95 } 96 /*for(int i = 1; i <= n; i ++) printf("%d ", f[i]); 97 printf("\n"); 98 for(int i = 1; i <= n; i ++) printf("%d ", g[i]); 99 printf("\n");*/ 100 for(R i = 0; i <= n; i ++) upmax(ans, f[i] + g[i + 1]); 101 printf("%d\n", ans); 102 } 103 104 int main() 105 { 106 freopen("in.in", "r", stdin); 107 pre(); 108 work(); 109 fclose(stdin); 110 return 0; 111 }
View Code