6401 创世纪 0x60「图论」例题
描述
上帝手中有 N(N≤10^6) 种世界元素,每种元素可以限制另外1种元素,把第 i 种世界元素能够限制的那种世界元素记为 A[i]。现在,上帝要把它们中的一部分投放到一个新的空间中去建造世界。为了世界的和平与安宁,上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素限制它。上帝希望知道,在此前提下,他最多可以投放多少种世界元素?
输入格式
第一行是一个整数N,表示世界元素的数目。
第二行有N 个整数A1, A2, …, AN。Ai 表示第i 个世界元素能够限制的世界元素的编号。
输出格式
一个整数,表示最多可以投放的世界元素的数目。
样例输入
6 2 3 1 3 6 5
样例输出
3
数据范围与约定
- 对于30%的数据:
- 对于60%的数据:
- 对于100%的数据:
样例解释
对于30% 的数据,N≤10。
对于100% 的数据,N≤10^6,1≤Ai≤N。
来源
石家庄二中【Nescafé 8】杯NOIP模拟赛
</article>
题解
基环树上上司的舞会。经典做法:两次DP,一次断开,一次强制链接(通过适当的条件和赋值实现)。
对于这题,可以预处理son子树中的DP值,然后分类时特判不再对son进行DP,赋值调用即可。
时间复杂度(O(n))
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=1e6+1,INF=0x3f3f3f3f;
int n,fa[N],t,k,f[N][2],s[N][2],ans;
int Head[N],Edge[N*2],Next[N*2],tot;
int get(int x) {return fa[x]==x?x:fa[x]=get(fa[x]);}
il void add(int x,int y){
Edge[++tot]=y,Next[tot]=Head[x],Head[x]=tot;
}
void dfs(int x){
int num=INF;
f[x][0]=0;
for(int i=Head[x];i;i=Next[i]){
if(Edge[i]!=k) dfs(Edge[i]);
f[x][0]+=max(f[Edge[i]][0],f[Edge[i]][1]);
num=min(num,max(f[Edge[i]][0],f[Edge[i]][1])-f[Edge[i]][0]);
}
f[x][1]=f[x][0]+1-num;
}
int main(){
read(n);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=n;++i){
int x=read<int>();
int p=get(x),q=get(i);
if(p==q) s[++t][0]=x,s[t][1]=i;
else add(x,i),fa[q]=p;
}
for(int i=1;i<=t;++i){
k=0;
dfs(s[i][0]);
k=s[i][0];
dfs(s[i][1]);
int now=max(f[s[i][1]][0],f[s[i][1]][1]);
f[s[i][0]][1]=f[s[i][0]][0]+1;
dfs(s[i][1]);
ans+=max(now,f[s[i][1]][0]);
}
printf("%d
",ans);
return 0;
}