武大网络赛H题

4 篇文章 0 订阅

Werewolf

H.Werewolf
题意是:狼人杀,然后放逐投票的时候,每个人可以投一个对象,只有狼和好人之分,狼不能投狼,好人可以投好人也可以投狼,给出每个人投票的对象,问说最多能有多少头狼。
把n个人看成n个点,然后建图,最后的图肯定是一个由k个联通分块组成的,每个连通块里面是一棵树或者一棵树再加一条边。如果没有这条边我们直接树形dp就行了,但是我们可以先把这条边去掉,然后我们可以分两种情况进行讨论,1、被多余的那条边连接的那两个点只有一个点被选取了的情况;2、这两个点都不被选取;
按照上面两种情况,做三次dp,就可以得到正确的答案了

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG    MOD=1e9+7;
const double PI=acos(-1.0);
#define clr0(x) memset(x,0,sizeof x)
#define clrI(x) memset(x,-1,sizeof(x))
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
const int MAXN = 1e6 + 100 ;
struct Edge
{
    int u, to , next ;
}edge[MAXN<<2];
int n ;
int tot = 0 ;
int head[MAXN<<2] ;
int fa[500500] ;
int take[500500] ;
int Qd1 , Qd2 ;
int vis [500500] ;
int dp[500050][2] ;
int root[500500] ;
int Pre[500500] ;
int judge ;
int Rjudge ;
int Find(int x)
{
    vis[x] ++ ;
    if(vis[fa[x]] ) return fa[x] = fa[fa[x]];
    if (x == fa[x]) return x;
    return fa[x] = Find(fa[x]) ;
}
void Add(int u , int  v)
{
    edge[++tot].to = v ;
    edge[tot].u = u;
    edge[tot].next = head[u] ;
    head[u] = tot ;
}
void Init()
{
    tot = 0 ;
    clrI(head) ;
    clr0(take) ;
    clr0(dp) ;
    clr0(vis) ;
}
void Found(int pre , int u)
{
    vis[u] ++ ;
    int t = 0;
    for(int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].to ;
        if(v == pre )
        {
            t ++ ;
            if(t >= 2) Rjudge = 1;
            continue ;
        }
        if(vis[v])
        {
            Qd1 = v ;
            Qd2 = u ;
            continue ;
        }
        Found( u , v ) ;
    }
}
void dfs(int pre ,int u)
{
    dp[u][1] += 1;
    vis[u] ++ ;
    for(int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].to ;
        if(v == pre) continue ;
        if(vis[v] )
            continue ;
        dfs(u , v) ;
        if(  v == Qd1  && judge == 0  )
        {
            dp[u][0] += dp[v][1] ;
            dp[u][1] += 0  ;
        }
        else if(v == Qd2 && judge == 0)
        {
            dp[u][0] += dp[v][0] ;
            dp[u][1] += dp[v][0] ;
        }
        else if(v == Qd1 && judge == 1)
        {
            dp[u][0] += dp[v][0] ;
            dp[u][1] += dp[v][0] ;
        }
        else if(v == Qd2 && judge ==1 )
        {
            dp[u][0] += dp[v][1] ;
            dp[u][1] += 0;
        }
        else if(v == Qd1 && judge == 2)
        {
            dp[u][0] += dp[v][0] ;
            dp[u][1] += dp[v][0] ;
        }
        else if(v == Qd2 && judge == 2)
        {
            dp[u][0] += dp[v][0] ;
            dp[u][1] += dp[v][0] ;
        }
        else
        {
            dp[u][0] += max(dp[v][0] , dp[v][1] ) ;
            dp[u][1] += dp[v][0] ;
        }
    }
//    printf("%d %d %d\n",u,dp[u][0],dp[u][1]);
}
int main()
{
//    freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
//freopen("C:\\Users\\ZhangYuyang\\Desktop\\out2.txt","w",stdout);
    while(cin >> n)
    {
        Init() ;
        for(int i = 1; i<= n ; ++ i)
            fa[i] = i ;
        int v ;
        for(int i = 1; i<= n ;++i)
            scanf("%d",&v)  ,
            Add( i , v ) ,
            Add( v , i ) ,
            fa[i] = v;
        for(int i = 1; i <= n ;++i) if(!vis[i])fa[i] = Find(i) ;
        for(int i = 1 ; i <= n ;++i) fa[i] = Find(i) ,take[fa[i]] = 1;
        int ans = 0 ;
        clr0(vis) ;
        for(int i = 1; i<= n ;++ i)
        {
            if(take[i] == 0) continue ;
            Rjudge = 0 ;
            judge = -1 ;
            int tmp = 0 ;
            Qd1 = -1 , Qd2 = -1;
            Found( -1 , i ) ;
            if(Rjudge)
            {
                clr0(vis) ;
                dfs(-1 , i ) ;
                tmp = max(dp[i][0] , dp[i][1] ) ;
                ans += tmp ;
            }
            else
            {
                judge ++ ;
                clr0(vis) ;
                dfs( -1 , i ) ;
            if(Qd2 != i)
                tmp = max(dp[i][0] , dp[i][1] ) ;
            else tmp = dp[i][0] ;
                clr0(dp) ;
                clr0(vis) ;
                judge ++ ;
                dfs(-1 , i );
            if(Qd1 != i)
                tmp = max(max(dp [i][0] , dp[i][1]) , tmp ) ;
            else
                tmp = max(tmp , dp[i][0]) ;

                judge ++ ;
                clr0(dp) ;
                clr0(vis) ;
                dfs(-1, i)  ;
                tmp = max(tmp , max(dp[i][0] , dp[i][1]) ) ;

                ans += tmp ;
            }
//            printf("%d %d\n",Qd1,Qd2);
        }
        cout<<ans<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值