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 }
方法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 }