[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

 

posted @ 2018-10-17 22:36  ww3113306  阅读(207)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。