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 ...
随机推荐
- WordPress的SEO插件——WordPress SEO by yoast安装及使用
插件:WordPress SEO by yoast 使用方法: 做好网站SEO一直是站长们的愿望,说简单也简单,但是说难也难,因为需要注意的地方太多,一个不小心被百度K了你都不知道怎么回事.这里和大家 ...
- git常用命令速查表【转】
- python基础===拆分字符串,和拼接字符串
给定某字符,只需要保留其中的有效汉字或者字母,数字之类的.去掉特殊符号或者以某种格式进行拆分的时候,就可以采用re.split的方法.例如 ============================== ...
- Python下urllib2应用
#coding=utf-8 import urllib,urllib2 url = 'http://www.xxx.com' values = {'wd' : 'python', 'language' ...
- Hadoop2.5.2 安装部署
0x00 平台环境 OS: CentOS-6.5-x86_64 JDK: jdk-8u111-linux-x64 Hadoop: hadoop-2.5.2 0x01 操作系统基本设置 1.1 网络配置 ...
- 关于为什么某些C/C++环境下浮点数可以“正常”比较的问题
有师妹问浮点数比较的问题,然后有人展示了可以“正常”比较的例子,google了一堆东西如下,有空仔细读一读,整理整理 http://bytes.com/topic/c/answers/629184-p ...
- 【严蔚敏】【数据结构(C语言版)】 求n的阶乘
阶乘函数为: 使用递归即可求得. #include <stdio.h> #include <stdlib.h> int Fact(int m){ ) ; ); //递归求阶乘 ...
- WPS Office 2012 专业版 附正版序列号
WPS Office 2012 专业版 附正版序列号 首先说说WPS的研发历史沿革:1988年5月,一个名叫求伯君的程序员凭借一台386电脑写出了WPS 1.0,从此开创了中文字处理时代,并迅速占领中 ...
- Django学习笔记-2018.11.17
URL配置: 项目下的urls.py配置的为URL总路径,在使用第二种方法在app下配置的urls.py是在总路径下的分路径 Templates: DTL初步使用 render()函数支持dict类型 ...
- C++ string to char *
半伪代码: void main { string line; getline(ifs, line); replace(line.begin(), line.end(), ',', '\n'); rep ...