HDU 5575 Discover Water Tank 并查集 树形DP

题意:

有一个水槽,边界的两块板是无穷高的,中间有n-1块隔板(有高度),现有一些条件(i,y,k),表示从左到右数的第i列中,在高度为(y+0.5)的地方是否有水(有水:k = 1),问最多能同时满足多少个条件。范围:1e5

分析:

考虑按隔板的高度从小到大合并隔板相邻的两列,合并的时候新开一个节点,这个可以用并查集来做。

这样合并过来就会得到一棵树,接下来就考虑如何把询问塞进去,然后做树形DP。

对于一个询问,我们需要把它存入第i列对应的叶节点上的某个父亲那里去,这个可以用vector来做,具体是哪个父亲可以倍增地找(当然预处理的时候要求一遍lca),即高度恰好在某个隔板下,这样就可以树形DP了。

树形DP:设d[i]表示树上以节点i的子树最多能满足的条件数,f[i]表示树上以节点i为子树中节点i有水能满足的条件数,emp[i]表示树上的节点i处没有水能满足的条件数。

对于d[i],节点i处没水,就直接转移emp[i]+d[son[i][0]]+d[son[i][1]];节点i处有水,则其子树必然有水,而节点i处的条件的高度y也不是单一的,这样我们只需求一个最大的前缀和(即水淹到哪一个高度,满足的条件有多少,求一个最大值)就可以了。

对于f[i],f[son[i][0]]+f[son[i][1]]+节点i处有水的条件数。

答案为d[rt]。

-----------------------------------------------------------------------------------------------------------------------------------

另外网上的似乎有一种更容易写的方法,直接把询问建树,当然还是按列合并,算法的思路也是一致的,但是树形DP写起来很简单,就是一个递推式。

具体地说,就是把询问、隔板按高度从小到大排序,依次作为节点插入树中,若当前询问的高度,大于等于当前隔板的高度,就合并相邻两列,建一个新节点,如此建出一棵树。而DP的时候就不用特别的去求前缀和了,直接递推过来就可以。

程序:

方法1:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <string>
  5 #include <algorithm>
  6 #include <iostream>
  7 #include <vector>
  8 
  9 using namespace std;
 10 
 11 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
 12 #define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
 13 #define mset(a, b) memset(a, b, sizeof(a))
 14 #define pb push_back
 15 const int maxn = 2e5+10;
 16 int n, m;
 17 struct Node
 18 {
 19     int h, id;
 20     bool operator < (const Node &AI) const
 21     {
 22         return h == AI.h ? id < AI.id : h < AI.h;
 23     }
 24 }a[maxn];
 25 struct Query
 26 {
 27     int h, ty;
 28     Query(int h = 0, int ty = 0):
 29         h(h), ty(ty) {}
 30     bool operator < (const Query &AI) const
 31     {
 32         return h == AI.h ? ty < AI.ty : h < AI.h;
 33     }
 34 };
 35 int fa[maxn*2][20], bel[maxn*2], to[maxn][2], tip[maxn*2], bot[maxn*2], t_cnt;
 36 int emp[maxn*2], f[maxn*2], d[maxn*2];
 37 vector <Query> g[maxn*2];
 38 
 39 template <class TAT>
 40 void Ckmax(TAT &a, const TAT &b)
 41 {
 42     if (a < b) a = b;
 43 }
 44 
 45 int Getbel(int x)
 46 {
 47     return x == bel[x] ? x : bel[x] = Getbel(bel[x]);
 48 }
 49 
 50 void prepare()
 51 {
 52     sort(a+1, a+n);
 53     REP(i, 1, n) bel[i] = tip[i] = i, bot[i] = fa[i][0] = fa[i][1] =  to[i][1] = to[i][0] = 0, g[i].clear();
 54     t_cnt = n;
 55     REP(i, 1, n-1)
 56     {
 57         int x = Getbel(a[i].id), y = Getbel(a[i].id+1);
 58         t_cnt ++, g[t_cnt].clear();
 59         fa[tip[x]][0] = fa[tip[y]][0] = t_cnt;
 60         bot[t_cnt] = a[i].h;
 61         to[t_cnt][0] = tip[x], to[t_cnt][1] = tip[y];
 62         bel[x] = y;
 63         tip[y] = t_cnt;
 64     }
 65     bot[0] = 0x3fffffff;    
 66     REP(j, 1, 19)
 67         REP(i, 1, t_cnt)
 68             fa[i][j] = fa[fa[i][j-1]][j-1];//, printf("%d %d : %d\n", i, j, fa[i][j]);
 69 }
 70 
 71 void work()
 72 {
 73     mset(d, 0), mset(f, 0);
 74     REP(i, 1, t_cnt)
 75     {
 76         if (!g[i].empty())
 77         {
 78             sort(g[i].begin(), g[i].end());
 79             int tmp, sum, sz = g[i].size(), j = 0, t_j;
 80             d[i] = sum = emp[i]+((i > n) ? f[to[i][0]]+f[to[i][1]] : 0);
 81             while (j < sz)
 82             {
 83                 tmp = g[i][j].ty ? 1 : -1, t_j = j;
 84                 while (t_j+1 < sz && g[i][t_j+1].h == g[i][j].h)
 85                     t_j ++, tmp += (g[i][t_j].ty ? 1 : -1);
 86                 sum += tmp;
 87                 Ckmax(d[i], sum);
 88                 j = t_j+1;
 89             }
 90             f[i] = sum;
 91         }
 92         if (i > n)
 93         {
 94             Ckmax(d[i], emp[i]+d[to[i][0]]+d[to[i][1]]);
 95             Ckmax(f[i], f[to[i][0]]+f[to[i][1]]);
 96         }
 97 //        printf("%d %d\n", d[i], f[i]);
 98     }
 99 }
100 
101 int main()
102 {
103 //    freopen("a.in", "r", stdin);
104 //    freopen("a.out", "w", stdout);
105     int T, iCase = 0;
106     scanf("%d", &T);
107     while (T --)
108     {
109         scanf("%d %d", &n, &m);
110         REP(i, 1, n-1) scanf("%d", &a[i].h), a[i].id = i;
111         prepare();
112         mset(emp, 0);
113         REP(i, 1, m)
114         {
115             int x, h, ty;
116             scanf("%d %d %d", &x, &h, &ty);
117             DWN(j, 19, 0)
118                 if (bot[fa[x][j]] <= h) x = fa[x][j];
119             g[x].pb(Query(h, ty));
120             emp[x] += (ty == 0);
121         }
122         work();
123         printf("Case #%d: %d\n", ++iCase, d[t_cnt]);
124     }
125     return 0;
126 }
View Code

方法2:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <string>
  5 #include <algorithm>
  6 #include <iostream>
  7 #include <vector>
  8 
  9 using namespace std;
 10 
 11 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
 12 #define mset(a, b) memset(a, b, sizeof(a))
 13 #define max_(a, b) a > b ? a : b
 14 #define pb push_back
 15 
 16 const int maxn = 2e5+10;
 17 int n, m;
 18 struct Node
 19 {
 20     int h, id;
 21     Node (int h = 0, int id = 0):
 22         h(h), id(id) {}
 23     bool operator < (const Node &AI) const
 24     {
 25         return h == AI.h ? id < AI.id : h < AI.h;
 26     }
 27 }a[maxn];
 28 struct Query
 29 {
 30     int x, y, z;
 31     void read(){ scanf("%d %d %d", &x, &y, &z); }
 32     bool operator < (const Query &AI) const
 33     {
 34         return y == AI.y ? x < AI.x : y < AI.y;
 35     }
 36 }b[maxn];
 37 int f[maxn], g[maxn], dp[maxn*4][2];
 38 vector <int> e[maxn*4];
 39 
 40 int find_fa(int x) { return f[x] == x ? x : f[x] = find_fa(f[x]); }
 41 
 42 void dfs(int x)
 43 {
 44     for (int i = 0, siz = e[x].size(); i < siz; ++i)
 45     {
 46         int y = e[x][i];
 47         dfs(y);
 48         dp[x][0] += max_(dp[y][1], dp[y][0]);
 49         dp[x][1] += dp[y][1];
 50     }
 51 }
 52 
 53 int main()
 54 {
 55 //    freopen("a.in", "r", stdin);
 56 //    freopen("a.out", "w", stdout);
 57     int T, iCase = 0;
 58     scanf("%d", &T);
 59     while (T --)
 60     {
 61         scanf("%d %d", &n, &m);
 62         REP(i, 1, n-1) scanf("%d", &a[i].h), a[i].id = i;
 63         REP(i, 1, m) b[i].read();
 64         REP(i, 1, n)
 65         {
 66             f[i] = g[i] = i;
 67             e[i].clear(), dp[i][0] = dp[i][1] = 0;
 68         }
 69         sort(a+1, a+n), sort(b+1, b+m+1);
 70         int now = n, cnt_n = 1, cnt_m = 1;
 71         while (cnt_m <= m || cnt_n < n)
 72         {
 73             if (cnt_m == m+1 || cnt_n < n && a[cnt_n].h <= b[cnt_m].y)
 74             {
 75                 int x = find_fa(a[cnt_n].id), y = find_fa(a[cnt_n].id+1);
 76                 e[++now].clear(), dp[now][0] = dp[now][1] = 0;
 77                 e[now].pb(g[x]), e[now].pb(g[y]);
 78                 f[x] = y, g[y] = now;
 79                 cnt_n ++;
 80             }
 81             else
 82             {
 83                 int x = find_fa(b[cnt_m].x);
 84                 if (b[cnt_m-1].y == b[cnt_m].y && find_fa(b[cnt_m-1].x) == x)
 85                     dp[g[x]][b[cnt_m].z] ++;
 86                 else
 87                 {
 88                     e[++now].clear(), dp[now][0] = dp[now][1] = 0;
 89                     e[now].pb(g[x]);
 90                     g[x] = now;
 91                     dp[now][b[cnt_m].z] ++;
 92                 }
 93                 cnt_m ++;
 94             }
 95         }
 96         dfs(now);
 97         printf("Case #%d: %d\n", ++iCase, max_(dp[now][0], dp[now][1]));
 98     }
 99     return 0;
100 }
View Code
posted @ 2017-03-20 09:32  Splay  阅读(275)  评论(0编辑  收藏  举报