题目:

BZOJ1487

分析:

题目中给定的图一定是一棵仙人掌(每条边最多属于一个环),证明如下:

先考虑单独一个岛的情况。第一,一个岛一定是一张「弦图」,即任意一个大小超过 3 的环都至少有 1 条弦。否则,这个环上不相邻的两点就不存在公共朋友,不符合「有一个公共朋友」。

第二,不存在有一条边被超过一个三元环包含。否则,这些三元环上与这条边相对的顶点都与这条边的两端点相邻,不符合「只有一个公共朋友」。

所以,每条边最多属于一个三元环。而由于大小超过 3 的环的弦一定存在于至少两个三元环中,所以不存在大小超过 3 的环。所以,在同一个岛中,每条边最多属于一个环,即为一个仙人掌。

现在,在每个岛(仙人掌)中选出一点与特定的另外两点相连,形成一个环。显然,每条新边都不可能和原本的仙人掌森林中的边形成环,所以这些新边在且仅在这个新环中。综上所述,原图是一个仙人掌。

那么这就是一个仙人掌 DP 的板子了(雾) 。设 \(f[i][0/1]\) 表示点 \(i\) 没选 / 选了时它的「子树」(见下文)内的最大权值。如果是一棵树,那么这就是一个非常简单的 DP (不会做的自觉面壁)。

下面这段比较难理解,不理解的话可以自己画个图手跑 Tarjan 。

考虑 Tarjan 求点双联通分量的过程。定义一个环的「根」为这个环上 dfs 序最小的点(就是建圆方树的时候把整个点双联通分量加进圆方树那个点 —— 只是以此为例说明,并不说明要建圆方树,下同)。如果一个环上所有的点的深度都大于等于某个点,那么这个环就在这个点的「子树」中,否则不算。即,一个环上只有这个环的「根」的子树包含这个环,这个环上被选中的点的权值在环上只算进根的 \(f\) 值。

当回溯到环的「根」时(就是把整个点双联通分量插入圆方树的时候),环上其他点的 \(f\) 值都已经计算完毕(再次强调,这些值都与这个环上的除了自己以外的点无关)。现在问题变成了:有一个环,选环上点 \(i\) 的权值是 \(f[i][1]\),不选的权值是 \(f[i][0]\) ,不能选相邻点,求最大能获得的权值。特别地,根的权值同样直接就是当前根的 \(f\) 值,因为要考虑这个根已经处理过的其他子树的权值。此处不理解的话参考树的做法。这个问题可以设 \(g[i][0/1][0/1]\) 表示当前考虑到第 \(i\) 个点,第一个点没选 / 选了,第 \(i\) 个点没选 / 选了。这个不会做的请继续面壁。

好像就这么多了?完结撒花~

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int N = 1e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m, head[N], w[N], ecnt;
struct edge
{
int to, next;
}e[M << 1];
void add(const int a, const int b)
{
e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
}
int dfn[N], low[N], dfncnt, f[N][2];
bool vis[M << 1];
void Tarjan(const int u, const int from)
{
static int sta[M << 1], top;
dfn[u] = low[u] = ++dfncnt;
f[u][0] = 0, f[u][1] = w[u];
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if (vis[i] || (i ^ 1) == from)
continue;
sta[top++] = i;
if (dfn[v])
low[u] = min(low[u], dfn[v]);
else
{
Tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u])
{
if (sta[top - 1] == i)
{
f[u][0] += f[v][1], f[u][1] += f[v][0];
vis[sta[top - 1]] = vis[sta[top - 1] ^ 1] = true;
--top;
}
else
{
static int buf[N];
int cnt = 0, t;
do
{
t = sta[--top];
vis[t] = vis[t ^ 1] = true;
buf[cnt++] = e[t].to;
}
while (t != i);
static int dp[N][2][2];
dp[0][0][0] = dp[0][0][1] = f[u][0];
dp[0][1][0] = -INF, dp[0][1][1] = f[u][1];
for (int i = 1; i < cnt; i++)
for (int j = 0; j < 2; j++)
{
dp[i][j][0] = dp[i - 1][j][1] + f[buf[i]][0];
dp[i][j][1] = dp[i - 1][j][0] + f[buf[i]][1];
dp[i][j][1] = max(dp[i][j][1], dp[i][j][0]);
}
f[u][0] = dp[cnt - 1][0][1], f[u][1] = dp[cnt - 1][1][0];
}
}
}
}
f[u][1] = max(f[u][1], f[u][0]);
}
int work()
{
read(n), read(m);
memset(head, -1, sizeof(int[n + 1]));
for (int i = 0; i < m; i++)
{
int a, b;
read(a), read(b);
add(a, b), add(b, a);
}
for (int i = 1; i <= n; i++)
read(w[i]);
Tarjan(1, -1);
write(max(f[1][0], f[1][1]));
return 0;
}
}
int main()
{
freopen("1487.in", "r", stdin);
return zyt::work();
}

【BZOJ1487】[HNOI2009]无归岛(仙人掌 DP)的更多相关文章

  1. bzoj1487 [HNOI2009]无归岛

    Description Neverland是个神奇的地方,它由一些岛屿环形排列组成,每个岛上都生活着之中与众不同的物种.但是这些物种都有一个共同的生活习性:对于同一个岛 上的任意两个生物,他们有且仅有 ...

  2. BZOJ1487 [HNOI2009]无归岛 【仙人掌dp】

    题目链接 BZOJ1487 题解 就是一个简单的仙人掌最大权独立集 还是不会圆方树 老老实实地树形Dp + 环处理 #include<iostream> #include<cstdi ...

  3. 2019.02.07 bzoj1487: [HNOI2009]无归岛(仙人掌+树形dp)

    传送门 人脑转化条件过后的题意简述:给你一个仙人掌求最大带权独立集. 思路:跟这题没啥变化好吗?再写一遍加深记忆吧. 就是把每个环提出来分别枚举环在图中的最高点选还是不选分别dpdpdp一下即可,时间 ...

  4. 【BZOJ1487】[HNOI2009]无归岛(动态规划)

    [BZOJ1487][HNOI2009]无归岛(动态规划) 题面 BZOJ 洛谷 题解 哪来的这么多废话啊,直接说一个仙人掌得了. 然后就是要你求仙人掌最大独立集了.(随便蒯份原来的代码就过了) 不过 ...

  5. P4410 [HNOI2009]无归岛

    P4410 [HNOI2009]无归岛 显然这还是一个仙人掌图 对于同一个岛上的任意两个生物,他们有且仅有一个公共朋友 要求求最大独立集,和树形dp一样,遇到环时单独提出来处理一下就好了 #inclu ...

  6. 【刷题】BZOJ 1487 [HNOI2009]无归岛

    Description Neverland是个神奇的地方,它由一些岛屿环形排列组成,每个岛上都生活着之中与众不同的物种.但是这些物种都有一个共同的生活习性:对于同一个岛上的任意两个生物,他们有且仅有一 ...

  7. [HNOI2009]无归岛

    Description Neverland是个神奇的地方,它由一些岛屿环形排列组成,每个岛上都生活着之中与众不同的物种.但是这些物种都有一个共同的生活习性:对于同一个岛上的任意两个生物,他们有且仅有一 ...

  8. 【题解】HNOI2009无归岛

    这题真的是无语了,在哪个岛上根本就没有任何的用处……不过我是画了下图,感受到一定是仙人掌,并不会证.有谁会证的求解…… 如果当做仙人掌来做确实十分的简单.只要像没有上司的舞会一样树形dp就好了,遇到环 ...

  9. Luogu-4410 [HNOI2009]无归岛

    裸的仙人掌最大独立子集,结果一个zz的错误让我调了好久... \(-inf\)开始设为\(0x7fffffff\)结果\(A_i\)有负数一加就炸了 #include<cstdio> #i ...

随机推荐

  1. HadoopMapReduce运行机制

    1.map方法读取一个文件的一行记录进行分析,  输入:LongWritable(当前读取的文件位置), Text(内容) 2.map将读取到的信息进行分类,输入Context  (键值对)  ;作为 ...

  2. Win8系统如何关闭用户账户控制UAC

    按WIN+S,屏幕右侧出现搜索框,在搜索框中输入UAC,然后单击"更改用户账户控制设置"   然后把弹出的窗口改成"从不通知"就可以了  

  3. 是男人就下100层【第四层】——Crazy贪吃蛇(2)

    在上一篇<是男人就下100层[第四层]--Crazy贪吃蛇(1)>中我们让贪吃蛇移动了起来,接下来我们来实现让贪吃蛇能够绕着手机屏幕边线移动而且能够改变方向 一.加入状态并改动代码 首先我 ...

  4. 性能监控 -- 中间件性能监控【Weblogic控制台】

    通过WebLogic管理控制台可以实时获取各性能指标,通过控制台,可以对weblogic的性能及运行状况,发布的应用.资源等进行监视 1. 进入Weblogic管理控制台,单击服务器,选择一台需监控的 ...

  5. cocos2d-x CCSrollView 源代码,可循环的SrollView代码

    项目须要.写一个类似于iPhone上面时钟选择的可拉动式循环选择列表,通过集成CCScrollView并更改部分代码.实现了该功能. 假设想充分了解代码,请先阅读源码分析http://blog.csd ...

  6. conda安装速度慢解决办法

    注意,清华已经撤掉其ananconda源, 下面的方法已经失效,中科大源好像也不行,如果有解决办法烦请评论告诉我. conda config --add channels https://mirror ...

  7. SQLServer导出单表数据

    采用生成脚本---仅数据..   如果是部分数据,可以先把部分数据备份到一个表中 select * into .. from ...

  8. cell.getCellType有几种

    CellType 类型 值CELL_TYPE_NUMERIC 数值型 0CELL_TYPE_STRING 字符串型 1CELL_TYPE_FORMULA 公式型 2CELL_TYPE_BLANK 空值 ...

  9. HDU - 1863 畅通工程(最小生成树)

    d.m个村庄,n条路,计算出所有村庄畅通需要的最低成本. s.最小生成树 c.Prim算法:cost[a][b]和cost[b][a]都得赋值. /* Prim算法 Prim求MST 耗费矩阵cost ...

  10. [Usaco2009 MAR] Earthquake Damage 2

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1585 [算法] 一个最小割的经典模型 , 详见代码 时间复杂度 : O(dinic( ...