【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;
}
posted @ 2022-03-15 10:27  HinanawiTenshi  阅读(34)  评论(0编辑  收藏  举报