1. 程式人生 > >[Joyoi&tyvj 1940] 創世紀 {樹形dp+基環樹}

[Joyoi&tyvj 1940] 創世紀 {樹形dp+基環樹}

題目

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); }