[Joyoi&tyvj 1940] 创世纪 {树形dp+基环树}

版权声明:请大家斧正,如喜欢的话,为拙见点一个赞吧。 https://blog.csdn.net/qq_39897867/article/details/84674258

题目

http://www.joyoi.cn/problem/tyvj-1940


解题思路

我们可以用两次树形 d p dp 来代替基环树 d p dp ,两次 D P DP ,一次断开,一次强制连接(通过适当的条件和赋值实现)。


代码

#include<cstdio> 
#define rr register 
using namespace std;
const int N=1000010; 
struct node{int y,next;}a[N];
int n,tot,ans,d[N],head[N],mark,root,f[N],g[N],fa[N]; bool v[N]; 
inline int minn(int x,int y){if (x<y) return x; return y;}
void add(int x,int y){a[++tot]=(node){y,head[x]}; head[x]=tot;}
void check_ring(int x){v[x]=true; if (v[d[x]]) mark=x; else check_ring(d[x]);}
void dp(int x)
{
   v[x]=true; 
   f[x]=1; g[x]=2147483647/3; 
   if (x==root) g[x]=0; 
   for (rr int i=head[x];i;i=a[i].next)
   {
		int y=a[i].y; 
		if (i==mark||y==fa[x]) continue; 
		fa[y]=x; dp(y); 
		g[x]+=minn(f[y],g[y]); 
		g[x]=minn(g[x],f[x]+f[y]-1); 
		f[x]+=minn(f[y],g[y]); 
   }
   return; 
}	
int main()
{
	scanf("%d",&n); 
	for (rr int i=1;i<=n;i++) scanf("%d",&d[i]),add(d[i],i); 
	for (rr int i=1;i<=n;i++)
	{
		if (v[i]) continue; 
		check_ring(i); 
		root=d[mark]; dp(mark); 
		int now=f[mark]; 
		root=0; dp(mark); 
		ans+=minn(now,g[mark]); 
	}
	printf("%d",n-ans); 
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/84674258
今日推荐