1. 程式人生 > 其它 >【DP】AcWing 359. 創世紀

【DP】AcWing 359. 創世紀

創世紀差點創死我

分析

注意到題意中給出的每個點都能夠限制某個點,如果從圖論角度考慮,那麼可以想到基環樹。

又因為每個點 \(A[u]\) 都能夠限制某個點 \(u\),考慮將 \(A[u]\) 看作 \(u\) 的父節點,記為 \(fa[u]\)

建圖後,考慮如何處理環。

可以發現,對於基環樹上一個點 \(rt\) 以及它的父節點 \(fa[rt]\),如果不考慮它們之間的連邊,問題轉化為樹形問題,那麼我們直接在這棵樹上進行 dp 即可。

那麼,如果強制考慮連邊後的影響吶?注意到連邊發生影響當且僅當強制不選取 \(rt\),對此進行特判即可。

最後的問題是處理這個樹形 dp:

狀態表示:\(f[u, 0/1]\)

表示 \(u\) 所在的子樹在 \(u\) 選取 / 不選取的時候的最大價值。

轉移方程:當 \(u\) 不選取的時候,子節點選取或者不選取均可;而 \(u\) 選取時,至少一個子節點需要不選取,所以我們貪心地決策:求取在一個子節點強制不選取的前提下的最小損失 \(del\) 即可。

\[f[u, 0] = \sum_{son}\max(f[son, 0], f[son, 1]) \\ f[u, 1] = f[u, 0] + 1 - del \]

實現

// 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)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=1e6+5, M=N, INF=0x3f3f3f3f;

struct Edge{
	int to, next;
}e[M];

int h[N], tot;

void add(int u, int v){
	e[tot].to=v, e[tot].next=h[u], h[u]=tot++;
}

int n, fa[N];
bool vis[N];

int f[N][2];
int rt, fl;
void dp(int u){
	f[u][0]=f[u][1]=0;
	vis[u]=true;
	int del=INF;
	for(int i=h[u]; ~i; i=e[i].next){
		int go=e[i].to;
		if(go==rt) continue;
		
		dp(go);
		f[u][0]+=max(f[go][0], f[go][1]);
		del=min(del, max(f[go][0], f[go][1])-f[go][0]);
	}
	f[u][1]=f[u][0]-del+1;
	if(u==fa[rt] && fl) f[u][1]=f[u][0]+1; 
}

int main(){
	memset(h, -1, sizeof h);	
	cin>>n;
	rep(i,1,n){
		read(fa[i]);
		add(fa[i], i);
	}
	
	int ans=0;
	rep(i,1,n) if(!vis[i]){
		rt=i;
		while(!vis[fa[rt]]) vis[rt]=true, rt=fa[rt];
		
		fl=0;
		dp(rt);
		int res=max(f[rt][0], f[rt][1]);
		
		fl=1;
		dp(rt);
		res=max(res, f[rt][0]);
		ans+=res;
	}
	cout<<ans<<endl;
	
	return 0;
}