一个元素选,则限制其的元素必须至少一个不选。
所以我们把元素指向限制其的元素。建出图。
假如是棵树,则用树形dp即可。(类比经典树形dp舞池问题)
但这是一颗基环树(即最多一个环,因为每个点有且只有一个入边,且是n个点,n条边)
先找到每个联通块的唯一环。
然后把环断开,当成一棵树进行树形dp。
再考虑断开环的影响:比如断开x->y, 影响是:缺少了一条边x->y, 即x选,y不选的情况。那么我们让y固定不选,则x就可以选,且y其他儿子可任意选或不选。
加个flag标记判断跑2次树形dp即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 1e6+7;
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
int a[M],vs[M];
int rt;
bool f;
int dp[M][2];//以i为根的子树,i点投放/不投放, 最多投放元素个数
int fat[M];
void gao(int x)
{
// cout<<" ---- "<<x<<" "<<fat[x]<<endl;
int tp=-1e7;
vs[x]=1;
dp[x][0]=dp[x][1]=0;
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(y==rt)continue;
gao(y);
dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=max(dp[y][0],dp[y][1]);
tp=max(tp,dp[y][0]-max(dp[y][0],dp[y][1]));
}
if(f&&x==fat[rt])dp[x][1]++;
else dp[x][1]=dp[x][1]+tp+1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
add(a[i],i,1);//建反向边,方便后面树形dp
fat[i]=a[i];
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(vs[i])continue;
int id=i;
while(!vs[id])
{
vs[id]=1;
id=a[id];
}
rt=id;
int mx=0;
f=false;
gao(rt);
mx=max(mx,max(dp[rt][0],dp[rt][1]));
// cout<<dp[rt][0]<<" "<<dp[rt][1]<<" == = "<<endl;
f=true;
gao(rt);
mx=max(mx,dp[rt][0]);
// cout<<"wwqrreq rrwe "<<dp[rt][0]<<endl;
ans+=mx;
// cout<<" 00 0 0 "<<mx<<" "<<rt<<endl;
}
cout<<ans<<endl;
return 0;
}