题意:

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

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

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

  2. hdu 4514 并查集+树形dp

    湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tot ...

  3. Codeforces 1156D 0-1-Tree ( 并查集 || 树形DP )

    <题目链接> 题目大意: 给定一颗无向树,树的边权只要0/1两种情况,现在问你这棵树上存在多少对有序对<u,v>,满足u-->v的路径上,如果出现边权为1的边之后,就不能 ...

  4. HDU 4514 湫湫系列故事——设计风景线(并查集+树形DP)

    湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) To ...

  5. BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集+树形DP

    BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集. Description 奶牛们遭到了进攻!在他们的共和国里,有N(1 <= N <=50,000)个城市,由M(1 ...

  6. P2700逐个击破(并查集/树形dp)

    P2700 逐个击破 题目背景 三大战役的平津战场上,傅作义集团在以北平.天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜.为了就地歼敌不让其逃走,老毛同志 ...

  7. hdu 4750 Count The Pairs(并查集+二分)

    Problem Description With the 60th anniversary celebration of Nanjing University of Science and Techn ...

  8. HDU HDU1558 Segment set(并查集+判断线段相交)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1558 解题报告:首先如果两条线段有交点的话,这两条线段在一个集合内,如果a跟b在一个集合内,b跟c在一 ...

  9. hdu 1257 小希的迷宫 并查集

    小希的迷宫 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1272 D ...

随机推荐

  1. OGG相关操作

    参数文件详解: 1)truncate ogg 进程: Manager进程:manager进程是配置在源端和目标端 Extract(抽取)进程:部署在源端,用于捕获数据表或者日志中的数据文件: Pump ...

  2. imperva配置文件的导入导出

    imperva配置文件的导入导出 Full_expimp.sh       //进行备份 1导入 2导出 输入密码后 1 全部导出 是否想导出失败的数据 默认密码是system的密码 输入导出的路径 ...

  3. ansible 下lineinfile详细使用 【转】

    转自 ansible 下lineinfile详细使用 - 散人 - 51CTO技术博客http://zouqingyun.blog.51cto.com/782246/1882367 一.简述 这几天在 ...

  4. 全面了解 Nginx 主要应用场景

    前言 本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得.所以还请见谅,同时欢迎留言交流 N ...

  5. admin组件详解

    admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...

  6. 51Nod 1352 集合计数(扩展欧几里德)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1352 题目大意: 给出N个固定集合{1,N},{2,N-1} ...

  7. (二)Spring 之IOC 详解

    第一节:spring ioc 简介 IOC(控制反转:Inversion of Control),又称作依赖注入dependency injection( DI ),是一种重要的面向对象编程的法则来削 ...

  8. [Ext JS 4]后台自动产生图档

    前言 [Ext JS 4] 实战之将chart导出为png, jpg 格式的文件 承接上一篇, 我们可以做到在Browser端打开一个Chart,并导出为png或是jpg 等格式的图档. 但实际的需求 ...

  9. 转载:C++ typename的起源与用法

    转载:http://feihu.me/blog/2014/the-origin-and-usage-of-typename/#typename 侯捷在Effective C++的中文版译序中提到: C ...

  10. karma+requirejs

    下面的介绍以karma能正常运行为前提,看karma系列文章:http://www.cnblogs.com/laixiangran/tag/Karma/ 目录结构 步骤 安装 npm install ...