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:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define mset(a, b) memset(a, b, sizeof(a))
#define pb push_back
const int maxn = 2e5+;
int n, m;
struct Node
{
int h, id;
bool operator < (const Node &AI) const
{
return h == AI.h ? id < AI.id : h < AI.h;
}
}a[maxn];
struct Query
{
int h, ty;
Query(int h = , int ty = ):
h(h), ty(ty) {}
bool operator < (const Query &AI) const
{
return h == AI.h ? ty < AI.ty : h < AI.h;
}
};
int fa[maxn*][], bel[maxn*], to[maxn][], tip[maxn*], bot[maxn*], t_cnt;
int emp[maxn*], f[maxn*], d[maxn*];
vector <Query> g[maxn*]; template <class TAT>
void Ckmax(TAT &a, const TAT &b)
{
if (a < b) a = b;
} int Getbel(int x)
{
return x == bel[x] ? x : bel[x] = Getbel(bel[x]);
} void prepare()
{
sort(a+, a+n);
REP(i, , n) bel[i] = tip[i] = i, bot[i] = fa[i][] = fa[i][] = to[i][] = to[i][] = , g[i].clear();
t_cnt = n;
REP(i, , n-)
{
int x = Getbel(a[i].id), y = Getbel(a[i].id+);
t_cnt ++, g[t_cnt].clear();
fa[tip[x]][] = fa[tip[y]][] = t_cnt;
bot[t_cnt] = a[i].h;
to[t_cnt][] = tip[x], to[t_cnt][] = tip[y];
bel[x] = y;
tip[y] = t_cnt;
}
bot[] = 0x3fffffff;
REP(j, , )
REP(i, , t_cnt)
fa[i][j] = fa[fa[i][j-]][j-];//, printf("%d %d : %d\n", i, j, fa[i][j]);
} void work()
{
mset(d, ), mset(f, );
REP(i, , t_cnt)
{
if (!g[i].empty())
{
sort(g[i].begin(), g[i].end());
int tmp, sum, sz = g[i].size(), j = , t_j;
d[i] = sum = emp[i]+((i > n) ? f[to[i][]]+f[to[i][]] : );
while (j < sz)
{
tmp = g[i][j].ty ? : -, t_j = j;
while (t_j+ < sz && g[i][t_j+].h == g[i][j].h)
t_j ++, tmp += (g[i][t_j].ty ? : -);
sum += tmp;
Ckmax(d[i], sum);
j = t_j+;
}
f[i] = sum;
}
if (i > n)
{
Ckmax(d[i], emp[i]+d[to[i][]]+d[to[i][]]);
Ckmax(f[i], f[to[i][]]+f[to[i][]]);
}
// printf("%d %d\n", d[i], f[i]);
}
} int main()
{
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
int T, iCase = ;
scanf("%d", &T);
while (T --)
{
scanf("%d %d", &n, &m);
REP(i, , n-) scanf("%d", &a[i].h), a[i].id = i;
prepare();
mset(emp, );
REP(i, , m)
{
int x, h, ty;
scanf("%d %d %d", &x, &h, &ty);
DWN(j, , )
if (bot[fa[x][j]] <= h) x = fa[x][j];
g[x].pb(Query(h, ty));
emp[x] += (ty == );
}
work();
printf("Case #%d: %d\n", ++iCase, d[t_cnt]);
}
return ;
}
方法2:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define mset(a, b) memset(a, b, sizeof(a))
#define max_(a, b) a > b ? a : b
#define pb push_back const int maxn = 2e5+;
int n, m;
struct Node
{
int h, id;
Node (int h = , int id = ):
h(h), id(id) {}
bool operator < (const Node &AI) const
{
return h == AI.h ? id < AI.id : h < AI.h;
}
}a[maxn];
struct Query
{
int x, y, z;
void read(){ scanf("%d %d %d", &x, &y, &z); }
bool operator < (const Query &AI) const
{
return y == AI.y ? x < AI.x : y < AI.y;
}
}b[maxn];
int f[maxn], g[maxn], dp[maxn*][];
vector <int> e[maxn*]; int find_fa(int x) { return f[x] == x ? x : f[x] = find_fa(f[x]); } void dfs(int x)
{
for (int i = , siz = e[x].size(); i < siz; ++i)
{
int y = e[x][i];
dfs(y);
dp[x][] += max_(dp[y][], dp[y][]);
dp[x][] += dp[y][];
}
} int main()
{
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
int T, iCase = ;
scanf("%d", &T);
while (T --)
{
scanf("%d %d", &n, &m);
REP(i, , n-) scanf("%d", &a[i].h), a[i].id = i;
REP(i, , m) b[i].read();
REP(i, , n)
{
f[i] = g[i] = i;
e[i].clear(), dp[i][] = dp[i][] = ;
}
sort(a+, a+n), sort(b+, b+m+);
int now = n, cnt_n = , cnt_m = ;
while (cnt_m <= m || cnt_n < n)
{
if (cnt_m == m+ || cnt_n < n && a[cnt_n].h <= b[cnt_m].y)
{
int x = find_fa(a[cnt_n].id), y = find_fa(a[cnt_n].id+);
e[++now].clear(), dp[now][] = dp[now][] = ;
e[now].pb(g[x]), e[now].pb(g[y]);
f[x] = y, g[y] = now;
cnt_n ++;
}
else
{
int x = find_fa(b[cnt_m].x);
if (b[cnt_m-].y == b[cnt_m].y && find_fa(b[cnt_m-].x) == x)
dp[g[x]][b[cnt_m].z] ++;
else
{
e[++now].clear(), dp[now][] = dp[now][] = ;
e[now].pb(g[x]);
g[x] = now;
dp[now][b[cnt_m].z] ++;
}
cnt_m ++;
}
}
dfs(now);
printf("Case #%d: %d\n", ++iCase, max_(dp[now][], dp[now][]));
}
return ;
}
HDU 5575 Discover Water Tank 并查集 树形DP的更多相关文章
- HDU 5575 Discover Water Tank(左偏树)
https://vjudge.net/problem/HDU-5575 题意: 有一个水箱,被n-1块板子分成了n个部分,板子的高度不尽相同.现在有m次探测,每次探测在第x部分的y+0.5高度处是否有 ...
- hdu 4514 并查集+树形dp
湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tot ...
- Codeforces 1156D 0-1-Tree ( 并查集 || 树形DP )
<题目链接> 题目大意: 给定一颗无向树,树的边权只要0/1两种情况,现在问你这棵树上存在多少对有序对<u,v>,满足u-->v的路径上,如果出现边权为1的边之后,就不能 ...
- HDU 4514 湫湫系列故事——设计风景线(并查集+树形DP)
湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) To ...
- BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集+树形DP
BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集. Description 奶牛们遭到了进攻!在他们的共和国里,有N(1 <= N <=50,000)个城市,由M(1 ...
- P2700逐个击破(并查集/树形dp)
P2700 逐个击破 题目背景 三大战役的平津战场上,傅作义集团在以北平.天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜.为了就地歼敌不让其逃走,老毛同志 ...
- hdu 4750 Count The Pairs(并查集+二分)
Problem Description With the 60th anniversary celebration of Nanjing University of Science and Techn ...
- HDU HDU1558 Segment set(并查集+判断线段相交)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1558 解题报告:首先如果两条线段有交点的话,这两条线段在一个集合内,如果a跟b在一个集合内,b跟c在一 ...
- hdu 1257 小希的迷宫 并查集
小希的迷宫 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1272 D ...
随机推荐
- perl6正则 6: 大小写/空白/匹配所有符合
这个 :g 只能写在外面 m:g /re/
- 2. 数据库文件配置与简单操作 Model / M()
官方文档说明位置: Thinkphp/Conf/convention.php 内容说明如下: 'DB_TYPE' => '', // 数据库类型 'DB_HOST' => '', // 服 ...
- Linux内核中的Cache段
Linux内核中的Cache段 原文地址:http://blogold.chinaunix.net/u2/85263/showart_1743693.html 最近移植LEON3的内核时,了解了一些简 ...
- MySQL sleep过多解决方法
睡眠连接过多,会对mysql服务器造成什么影响? 严重消耗mysql服务器资源(主要是cpu, 内存),并可能导致mysql崩溃. 造成睡眠连接过多的原因? 1. 使用了太多持久连接(个人觉得,在高并 ...
- Python设计模式中单例模式的实现及在Tornado中的应用
单例模式的实现方式 将类实例绑定到类变量上 class Singleton(object): _instance = None def new(cls, *args): if not isinstan ...
- java基础11 继承(super、extends关键字和重写,这三个要素出现的前提:必须存在继承关系)
面向对象的三大特征: 1.封装 (将一类属性封装起来,并提供set()和get()方法给其他对象设置和获取值.或者是将一个运算方法封装起来,其他对象需要此种做运算时,给此对象调用) 2.继承 ...
- HDU 3018 Ant Trip (并查集求连通块数+欧拉回路)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3018 题目大意:有n个点,m条边,人们希望走完所有的路,且每条道路只能走一遍.至少要将人们分成几组. ...
- CentOS_Linux服务器系统安装之分区
在software selection中选择Server with GUI>(Compatibility Libraries.Development Tools和Security Tools) ...
- Python 一些 实用的包(持续更新)
line_profiler:(代码性能分析) 使用方法:链接 codecs:(Python内置的编码库) 数据分析与挖掘领域: 引自博客:这里 因为他有很多这个领域相关的库可以用,而且很好用, ...
- day4 使用yield实现单线程
一.yield生成器(yield) yield用来结束while循环,并且能够保持之前循环的状态,下一次调用的时候直接从yield开始执行,执行yield后面的程序,并且重新进行循环:另外,yield ...