nssl1270-創世紀【樹形dp,基環樹】
阿新 • • 發佈:2018-11-05
正題
題目大意
每個物品有一個可以限制的物品,要求一個集合內所有的物品都有一個不在集合內物品限制。求這個集合可以保護的最多物品
解題思路
類似沒有上司的舞會
其實就是在基環樹森林,我們可以利用二次樹形dp的方法。
先找到環,然後強行將環斷開進行一次dp,然後強行連上進行一次dp,兩個答案的最小值就得這棵樹的最大物品。
其實也可以貪心,這裡就不放了。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000010
using namespace std;
struct node{
int to,next;
}a[N];
int n,x,ans,tot,fa[N],root,f[N],g[N],ls[N],d[N],mark;
bool v[N];
void addl(int x,int y)
{
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
}
void check_c(int x)//判環
{
v[x]=true;
if(v[d[x]]) mark=x;
else check_c(d[x]);
return;
}
void dp(int x)//樹形dp
{
v[ x]=true;
f[x]=1;g[x]=2147483647/3;
if(x==root) g[x]=0;
for(int i=ls[x];i;i=a[i].next)
{
int y=a[i].to;
if(i==mark||y==fa[x]) continue;
fa[y]=x;
dp(y);
g[x]+=min(f[y],g[y]);
g[x]=min(g[x],f[x]+f[y]-1);
f[x]+=min(f[y],g[y]);
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n;i++)
scanf("%d",&d[i]),addl(d[i],i);
for(int i=1;i<=n;i++)
{
if(v[i]) continue;
check_c(i);
root=d[mark];dp(mark);//斷開環
int maxs=f[mark];
root=0;dp(mark);//連線環
ans+=min(maxs,g[mark]);
}
printf("%d",n-ans);
}