tyvj 创世纪 - 基环树
codevs : 传送门
Description
上帝手中有着N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界。
每种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世界元素都有至少一个没有被投放的
世界元素能够限制它,这样上帝就可以保持对世界的控制。由于那个著名的有关于上帝能不能制造一块连自己
都不能举起的大石头的二律背反命题,我们知道上帝不是万能的,而且不但不是万能的,他甚至有事情需要找
你帮忙——上帝希望知道他最多可以投放多少种世界元素,但是他只会O(2N) 级别的算法。虽然上帝拥有无限多
的时间,但是他也是个急性子。你需要帮助上帝解决这个问题。
题解
把$A[ i ]$ 当作 $f[ i ]$ 即父节点
dfs找环 + 断环 + 重连
只需找到环上的任意两个点记录即可
找到了$x, y$ , 并且$A_x = y$
断环: 让找到的两个点之间的边断开, 从子节点开始$dp$, 算出投放 $x$时的最大值即 $f[x][1]$
重连: 当然不可能真的重连,只需要让 $x$ 不被投放, 并且在处理$y$时, $y$的子节点无需限制它, 因为已经有$x$在限制了
相当于重连
记得手工栈, 不然会RE
注意一个点组成的环需要一些特判,不然会WA
代码
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define rd read()
#define rep(i,a,b) for(register int i = (a); i <= (b); ++i)
#define per(i,a,b) for(register int i = (a); i >= (b); --i)
#define R register
using namespace std; const int N = 1e6 + 1e3; int n, m, fa[N], vis[N];
int f[N][], pos1, pos2, eg;
int tot, head[N]; struct edge {
int to, nxt;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for(; c > '' || c < ''; c = getchar()) if( c== '-') p = -;
for(; c >= '' && c <= ''; c = getchar()) X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
} int lev;
int st_x[N];
int st_nt[N];
int st_tmp[N]; #define x st_x[lev]
#define nt st_nt[lev]
#define tmp st_tmp[lev] void find_cir(int u) {
st_x[] = u;
lev = ;
start:;
vis[x] = ;
nt = fa[x];
if(vis[nt]) {
pos1 = x; pos2 = nt;
}
else {
st_x[lev + ] = nt;
lev++;
goto start;
}
end:;
if((--lev)) goto end;
} int st_i[N]; #define i st_i[lev] void dp(int u) {
st_x[] = u;
lev = ;
start:;
tmp = ;
f[x][] = f[x][] = ;
vis[x] = ;
for(i = head[x]; i; i = e[i].nxt) {
nt = e[i].to;
if(nt == pos1) continue;
st_x[lev + ] = nt;
lev++;
goto start;
end:; f[x][] = f[x][] + max(f[nt][], f[nt][]);
tmp += max(f[nt][], f[nt][]);
}
for(i = head[x]; i; i = e[i].nxt) {
nt = e[i].to;
if(nt == pos1) continue;
f[x][] = max(f[x][], + tmp - max(f[nt][], f[nt][]) + f[nt][]);
}
if(--lev) goto end;
} void dp2(int u) {
st_x[] = u;
lev = ;
start:;
tmp = ;
f[x][] = f[x][] = ;
for(i = head[x]; i; i = e[i].nxt) {
nt = e[i].to;
if(nt == pos1) continue;
st_x[lev + ] = nt;
lev++;
goto start;
end:; f[x][] = f[x][] + max(f[nt][], f[nt][]);
if(x == pos2 && pos1 != pos2) f[x][] = f[x][] + max(f[nt][], f[nt][]);
tmp += max(f[nt][], f[nt][]);
}
if(x == pos2 && pos1 != pos2) {
f[x][]++;
if(--lev) goto end;
}
for(i = head[x]; i; i = e[i].nxt) {
nt = e[i].to;
f[x][] = max(f[x][], + tmp - max(f[nt][], f[nt][]) + f[nt][]);
}
if(--lev) goto end;
} #undef x
#undef i
#undef nt
#undef tmp int work(int x) {
int maxn = ;
find_cir(x);
dp(pos1);
maxn = f[pos1][];
dp2(pos1);
maxn = max(maxn, f[pos1][]);
return maxn;
} int main()
{
n = rd;
int ans = ;
rep(i, , n) fa[i] = rd, add(fa[i], i);
rep(i, , n) if(!vis[i]) ans += work(i);
printf("%d\n", ans);
}
tyvj 创世纪 - 基环树的更多相关文章
- BZOJ3037 创世纪[基环树DP]
实际上基环树DP的名字是假的.. 这个限制关系可以看成每个点有一条出边,所以就是一个内向基环树森林. 找出每个基环树的环,然后对于树的部分,做DP,设状态选或不选为$f_{x,0/1}$,则 $f_{ ...
- Solution -「基环树」做题记录
写的大多只是思路,比较简单的细节和证明过程就不放了,有需者自取. 基环树简介 简单说一说基环树吧.由名字扩展可得这是一类以环为基础的树(当然显然它不是树. 通常的表现形式是一棵树再加一条非树边,把图画 ...
- [bzoj3037/2068]创世纪[Poi2004]SZP_树形dp_并查集_基环树
创世纪 SZP bzoj-3037/2068 Poi-2004 题目大意:给你n个物品,每个物品可以且仅可以控制一个物品.问:选取一些物品,使得对于任意的一个被选取的物品来讲,都存在一个没有被选取的物 ...
- tyvj1940创世纪——贪心(基环树)
题目:http://www.joyoi.cn/problem/tyvj-1940 基环树的样子,看了书上的讲解,准备写树上DP,然后挂了: #include<iostream> #incl ...
- BZOJ3037 创世纪(基环树DP)
基环树DP,攻的当受的儿子,f表选,g表不选.并查集维护攻受关系.若有环则记录,DP受的后把它当祖宗,再DP攻的. #include <cstdio> #include <iostr ...
- TYVJ 1940 创世纪
Description: 上帝手中有着 N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界.每 种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世 ...
- Poetize4 创世纪
3037: 创世纪 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 123 Solved: 66[Submit][Status] Description ...
- 【BZOJ3037/2068】创世纪/[Poi2004]SZP 树形DP
[BZOJ3037]创世纪 Description applepi手里有一本书<创世纪>,里面记录了这样一个故事……上帝手中有着N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放 ...
- CH6401 创世纪
6401 创世纪 0x60「图论」例题 描述 上帝手中有 N(N≤10^6) 种世界元素,每种元素可以限制另外1种元素,把第 i 种世界元素能够限制的那种世界元素记为 A[i].现在,上帝要把它们中的 ...
随机推荐
- SQL Select语句完整的执行顺序(转)
SQL Select语句完整的执行顺序: 1.from子句组装来自不同数据源的数据: 2.where子句基于指定的条件对记录行进行筛选: 3.group by子句将数据划分为多个分组: 4.使用聚集函 ...
- DB2 57016报错的解决办法(表状态不正常,导致表无法操作)
新建了一张表,删除了一列,然后执行insert的时候,报错 57016,解释为:因为表不活动. 1.执行db2 "load query table <tabname>" ...
- openvpn 使用账户密码认证
OpenVPN使用user/passwd完成验证登录 OpenVPN使用user/passwd完成验证登录1,为什么要使用user/passwd?比常规openvpn管理方便,删除用户只需要在pwd. ...
- YouTube Cobalt 浏览器支持
Cobalt介绍: Cobalt浏览器是YouTube公司定制的一款专用浏览器,Cobalt的使命,是在电视机端,使用灵活多变web形式实现流畅的交互操作,从而替代Android,与普通浏览器不同,C ...
- python基础入门学习1
python比较 -python执行效率低,开发效率高. -JAVA执行效率高,开发效率低. python种类多:比如Jpython Cpython pypy(这是Cpython开发的python) ...
- “东信杯”广西大学第一届程序设计竞赛(同步赛)H
链接:https://ac.nowcoder.com/acm/contest/283/H来源:牛客网 题目描述 由于临近广西大学建校90周年校庆,西大开始了喜闻乐见的校园修缮工程! 然后问题出现了,西 ...
- HDU 3974 Assign the task(DFS序+线段树单点查询,区间修改)
描述There is a company that has N employees(numbered from 1 to N),every employee in the company has a ...
- [leetcode]311. Sparse Matrix Multiplication 稀疏矩阵相乘
Given two sparse matrices A and B, return the result of AB. You may assume that A's column number is ...
- 建立SSH的信任关系
1.在Client上root用户执行ssh-keygen命令,生成建立安全信任关系的证书. Client端 # ssh-keygen -t rsa Generating public/private ...
- Java使用点滴
1.查找某个字符在字符串中第几次出现的位置 /** * 查找某个字符在字符串中第几次出现的位置 * @param string 要匹配的字符串 * @param i 第几次出现 * @param ch ...