题意:
狼人杀投票环节,n个人进行投票,狼一定不会投狼,人可以任意投(除自己),现给出每个人的投票情况,问最多可能会有多少头狼。
思路:
比赛的时候没有多想,一直用树型DP去写,后来发现会有环的情况出现,没有想到解决办法。。。
赛后被题解点拨一下,发现两轮DFS可以解决环的情况,第一轮判断当前点所在的图有没有环,如果有,找出环上相邻的两个点,然后第二轮分别枚举环上的相邻的两个点作为起点和终点,终点不能选做狼人,然后就是基础的树型DP的模型。。。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
vector<int>G[500005];
int n;
int a,b;
int dp[500005][2];
bool vis[500005];
bool VIS[500005];
vector< pair<int,int> >V;
void dfs(int x,int fa=0){
VIS[x]=true;
for(int i=0;i<G[x].size();i++){
int &j = G[x][i];
if(j==fa)continue;
if(VIS[j]){
a = x ;
b = j ;
}else {
dfs(j,x);
}
}
}
void dfs2(int x){
vis[x]=true;
dp[x][1]=(x!=b);
dp[x][0]=0;
for(int i=0;i<G[x].size();i++){
int &j = G[x][i];
if(x==a&&j==b&&i!=G[x].size()-1)continue;
if(!vis[j]){
dfs2(j);
dp[x][1] += dp[j][0];
dp[x][0] += max(dp[j][1],dp[j][0]);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
V.emplace_back( pii(min(i,x),max(i,x)) );
}sort(V.begin(),V.end());
pii t ;
for(int i=0;i<V.size();i++){
if(!i||V[i]!=V[i-1]){
t = V[i];
G[t.fi].emplace_back(t.se);
G[t.se].emplace_back(t.fi);
}
}
int ans = 0;
for(int i=1;i<=n;i++){
if(VIS[i])continue;
a = i;b = 0;
dfs(i);
memset(vis,0,sizeof(vis));
dfs2(a);
int t = max(dp[a][1],dp[a][0]);;
if(b){
swap(a,b);
memset(vis,0,sizeof(vis));
dfs2(a);
t = max(t,max(dp[a][1],dp[a][0]));
}
ans += t;
}printf("%d\n",ans);
return 0;
}