[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 }