359. 创世纪
题目链接
359. 创世纪
上帝手中有 \(N\) 种世界元素,每种元素可以限制另外 \(1\) 种元素,把第 \(i\) 种世界元素能够限制的那种世界元素记为 \(A[i]\)。
现在,上帝要把它们中的一部分投放到一个新的空间中去建造世界。
为了世界的和平与安宁,上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素限制它。
上帝希望知道,在此前提下,他最多可以投放多少种世界元素?
输入格式
第一行是一个整数 \(N\),表示世界元素的数目。
第二行有 \(N\) 个整数 \(A[1], A[2], …, A[N]\)。\(A[i]\) 表示第 \(i\) 个世界元素能够限制的世界元素的编号。
输出格式
一个整数,表示最多可以投放的世界元素的数目。
数据范围
\(N \le 10^6,1 \le A[i] \le N\)
输入样例:
6
2 3 1 3 6 5
输出样例:
3
解题思路
基环树dp
对于树形的结构,考虑树形dp:
-
\(f[i][0]\) 表示不选 \(i\) 的最大值
-
\(f[i][1]\) 表示选 \(i\) 的最大值
反向建边,对于 \(f[i][0]\),由于不选 \(i\),则其子节点可选可不选,即 \(f[i][0]=max\{f[j][0],f[j][1]\}\),对于 \(f[i][1]\),由于选 \(i\),则必须得有一个 \(j\) 不选,即 \(f[j][0]\),\(f[i][1]=max\{\sum_{k\neq j} max(f[k][0],f[k][1])+f[j][0]\}=max\{f[i][0]-max(f[j][0],f[j][1])+f[j][0]\}\)
对于基环树,考虑删边,对于 \(x\rightarrow y\) 这条边,即 \(x\) 限制 \(y\)(但建图反向建边),分情况讨论这条边用没用上,当这条边没用上时,则直接删除即可,这时 \(x\) 可选可不选,当这条边用上时,即 \(x\) 必须选,\(y\) 必须不选,转移时限制这两个状态即可,对于 \(f[x][1]\) 这个状态,首先应该先更新所有的 \(f[u][0]\),由于选择 \(x\) 对其他节点没有影响,则此时 \(f[x][1]=f[x][0]+1\),即在原来的基础上加上 \(x\) 这个被选的节点,然后在限制非法状态即可
- 时间复杂度:\(O(n)\)
代码
// Problem: 创世纪
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/361/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e6+5;
int n;
int h[N],e[N],ne[N],idx;
int f1[N][2],f2[N][2],res;
bool st[N],in[N],rm[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int x,int u,int f[N][2])
{
for(int i=h[x];~i;i=ne[i])
{
if(rm[i])continue;
int y=e[i];
dfs(y,u,f);
f[x][0]+=max(f[y][0],f[y][1]);
}
if(x==u)f[x][1]=f[x][0]+1,f[x][0]=-0x3f3f3f3f;
else
{
for(int i=h[x];~i;i=ne[i])
{
if(rm[i])continue;
int y=e[i];
f[x][1]=max(f[x][1],f[x][0]-max(f[y][0],f[y][1])+f[y][0]+1);
}
}
}
void dfs_c(int x)
{
st[x]=in[x]=true;
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(!st[y])dfs_c(y);
else if(in[y])
{
rm[i]=true;
dfs(y,-1,f1);
dfs(y,x,f2);
res+=max({f1[y][0],f1[y][1],f2[y][0]});
}
}
in[x]=false;
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
add(a,i);
}
for(int i=1;i<=n;i++)
if(!st[i])dfs_c(i);
printf("%d",res);
return 0;
}