题意:

有一个水槽,边界的两块板是无穷高的,中间有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

转载于:https://www.cnblogs.com/-ZZB-/p/6585867.html

HDU 5575 Discover Water Tank 并查集 树形DP相关推荐

  1. HDU 5575 Discover Water Tank 并查集+左偏树

    不妨假定初始答案为所有的无水询问,因为这样一定没有冲突. 然后枚举有水询问.水位线到这里时,答案能否更优. 若水位线达到某一高度,则可能淹没旁边的水箱,那么实际就变成了一个大水箱,所以考虑用并查集来优 ...

  2. HDU 5575 Discover Water Tank(线段树+自底向上dp+并查集)

    题意 给定n个挡板和m次回答,每次回答为x号水池的H+0.5高度是否有水,问这些回答互不矛盾的最大集合. 题解 设定状态dp[i][0-1]代表第i个区间枚举到当前回答后有水的最大不矛盾集合和没水的最 ...

  3. hdu 5575 Discover Water Tank(可合并堆)

    题目链接:hdu 5575 Discover Water Tank 题意: 有一个大水箱,里面有N-1个隔板,将这个大水箱分成了N个小水箱,每个隔板有一定的高度. 现在有m条信息,每条信息表示第x个水 ...

  4. hdu 5575 Discover Water Tank 左偏树

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5575 题意: 现在有一个巨大的水库(可视为二维的),水库中间被 n−1n-1n−1 个挡板分成了 n ...

  5. HDU 5575 Discover Water Tank

    原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=5575 把每个水箱当作一个并查集,每个无水的探测当做一个左偏树.有水的探测则放在数组中.并用一个数组使水 ...

  6. HDU 5575 Discover Water Tank(左偏树)

    https://vjudge.net/problem/HDU-5575 题意: 有一个水箱,被n-1块板子分成了n个部分,板子的高度不尽相同.现在有m次探测,每次探测在第x部分的y+0.5高度处是否有 ...

  7. 图论500题 ---- 并查集+树形dp+枚举 求解动态的最小生成树 HDU 4126

    题目链接 题目大意: 给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边(可以经过可疑边新的花费构建的边),注意每次只出现一条 ...

  8. HDU5575 Discover Water Tank 2015上海现场赛D题 (树形dp,并查集,左偏树)

    题目大意: 有一个1维的长度为N,高度无限的水柜,现在要用N-1个挡板将其分为N个长度为1的小格,然后向水柜中注水,水可以低于挡板也可以以溢出去(这样就要与旁边格子的水位相同),现在有M次探测,探测i ...

  9. 树形DP+并查集+左偏树, HDU-5575,Discover Water Tank,2015上海现场赛D题

    只是ACM/IICPC 2015 上海区域赛的一道题.原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5575 题目描述 N-1个木板把一个水箱划分成了N ...

最新文章

  1. 高通将用芯片改变无人机行业格局
  2. office 2013 安装问题
  3. 八十三、经典排序算法之堆排序
  4. BZOJ 4517 组合数+错排
  5. c++获取macos中的uuid的两种方式
  6. php 生成器 封装,php生成器
  7. windows 快捷调用
  8. sql server中case的简单示例
  9. 制作png格式透明图片的简易方法
  10. 一篇散文简单的了解Redis
  11. 计算机cpu intel,intel CPU后面带F是什么意思?Intel处理器后面带“F”含义详解
  12. Android群英传 笔记1 安卓 系统架构和开发工具
  13. python找房源_python抓取链家房源信息(三)
  14. MacOS / Vmware Fusion无法连接虚拟设备sata0:1,因为主机上没有相应设备
  15. java esc_java里控制台按esc键退出,怎么实现
  16. 如何使用Graylog来收集日志?
  17. JsonFormat使用经历
  18. Robin六种常用负载均衡算法源码解析
  19. 如何做医美直播?从直播的这三个阶段出发
  20. A+成绩入选纳斯达克100指数 中国版DocuSign何时诞生?

热门文章

  1. spark 内存管理
  2. 全国省市区json数据
  3. Cortex M0下coos调度上下文切换原理
  4. 河北光伏巨头再布局 晶龙投2.8亿美元在越南建厂
  5. 是男人就挺过二十秒源代码
  6. 游戏原画学习方法_学习游戏原画有什么方法
  7. CPen绘制线条宽度大于1 的虚线
  8. jiamimao – 文件加密最简法门
  9. Matlab:停止执行
  10. 关于以太坊合并你应该知道的15个知识点