题解:P3472 [POI 2008] MAF-Mafia
Solution
考虑建图,从
题目等价于保留若干个节点,使得这些节点均不相邻,即独立集问题。
这是因为,若两个点是相邻的,则其中的父亲
首先,死去的人最多是好做的,只需要保留必要的节点(入度为
把环视为树根,首先预处理出树根的每个子树的独立集的贡献,自下往上,记
可能有些节点的一些状态是不能到达的,设成
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=2e6+10,mod=998244353;
typedef pair<int,int> pii;
typedef long long ll;
/*
*/
int C,T=1;
int s[N],n,deg[N],dp[N][2];
int f[N][2][2],fu[N],cnt[N];
int fnd(int u) {return u==fu[u]?u:fu[u]=fnd(fu[u]);}
void merge(int u,int v){u=fnd(u),v=fnd(v);if(u!=v) fu[u]=v;}
void Lain(){
cin>>n;
for(int i=1;i<=n;i++) fu[i]=i;
for(int i=1;i<=n;i++)
cin>>s[i],deg[s[i]]++,merge(i,s[i]);
queue<int> q;
for(int i=1;i<=n;i++) if(!deg[i])
q.push(i),dp[i][0]=-1,++cnt[fnd(i)];
while(!q.empty()){
int u=q.front(),v=s[u]; q.pop();
if(~dp[u][1]) ++dp[u][1];
if((~dp[u][1]||~dp[u][0])&&~dp[v][0]) dp[v][0]+=max(dp[u][1],dp[u][0]);
if(dp[u][0]==-1) dp[v][1]=-1;
else if(~dp[v][1]) dp[v][1]+=dp[u][0];
--deg[v]; if(!deg[v]) q.push(v);
}
ll res=0,rm=0;
for(int i=1;i<=n;i++) if(fnd(i)==i){
rm+=cnt[i];
if(!cnt[i]&&s[i]!=i) rm++;
}
for(int i=1;i<=n;i++) if(s[i]==i)
deg[i]=0,res+=dp[i][0];
for(int i=1;i<=n;i++) if(deg[i]){ deg[i]=0;
f[i][0][1]=f[i][1][0]=-1; int u=s[i];
f[i][0][0]=dp[i][0],f[i][1][1]=dp[i][1];
while(u!=i){ deg[u]=0;
for(int p:{0,1})
f[u][1][p]=dp[u][1],
f[u][0][p]=dp[u][0];
u=s[u];
}
while(s[u]!=i){
int v=s[u];
for(int p:{0,1}){
if(~f[u][1][p]) ++f[u][1][p];
if((~f[u][1][p]||~f[u][0][p])&&~f[v][0][p]) f[v][0][p]+=max(f[u][1][p],f[u][0][p]);
if(f[u][0][p]==-1) f[v][1][p]=-1;
else if(~f[v][1][p]) f[v][1][p]+=f[u][0][p];
}
u=v;
}
if(~f[u][1][0]) f[u][1][0]++;
int tmp=max({f[u][1][0],f[u][0][1],f[u][0][0]});
if(~tmp) res+=tmp;
}
cout<<n-res<<" "<<n-rm;
return;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
// cin>>C>>T;
while(T--) Lain();
return 0;
}