【DP】AcWing 359. 創世紀
阿新 • • 發佈:2022-03-15
創世紀差點創死我
分析
注意到題意中給出的每個點都能夠限制某個點,如果從圖論角度考慮,那麼可以想到基環樹。
又因為每個點 \(A[u]\) 都能夠限制某個點 \(u\),考慮將 \(A[u]\) 看作 \(u\) 的父節點,記為 \(fa[u]\)。
建圖後,考慮如何處理環。
可以發現,對於基環樹上一個點 \(rt\) 以及它的父節點 \(fa[rt]\),如果不考慮它們之間的連邊,問題轉化為樹形問題,那麼我們直接在這棵樹上進行 dp 即可。
那麼,如果強制考慮連邊後的影響吶?注意到連邊發生影響當且僅當強制不選取 \(rt\),對此進行特判即可。
最後的問題是處理這個樹形 dp:
狀態表示:\(f[u, 0/1]\)
轉移方程:當 \(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; }