WOJ-29 Werewolf(DP)

Input file: standard input
Output file: standard output 
Time limit: 1 second
Memory limit: 512 mebibytes

Generally, ACMers play Werewolf game anywhere when they go out for any programming contests. There are several roles in this game, including werewolves, villagers, seer, witch, hunter, cupid……

Here is the rule of the game:

The werewolves Each night the werewolves kill a player.

The villagers The villagers has no skills.

The seer Each night the seer can look at a card of a player of his choice to find out the real.

The hunter If the hunter gets killed, the hunter has the power to retaliate by killing a player of his choice immediately.

The cupid On the first night, cupid picks 2 players who he denotes as lovers. Those 2 players will fall madly in love with each another. If one of the lover dies, the other, out of sadness, dies immediately.

The witch The witch knows how to make up 2 extremely powerful potions. One healing potion, which can revive the player that has been killed by the werewolves. One poison potion, which when used at night can kill a player. The witch must use each potion only once during the game.

...

After each night, everyone wakes up, the god(game master) shows all players which player was killed during the night. After discussion, each player must select one player that they want to eliminate when they vote.

...

Generally, the werewolves do not select their teammates, but other roles do select anyone when voting. Here comes the problem. How many werewolves at most under the given voting case.

Input

The first line of input file consists of an integer nn(2\le n\le 5000002n500000), the number of players.

The second line of input file consists of nn integers, and the ii-th number x_ixi(1\le x_i\le n, x_i \ne i1xin,xii) represents the number of the player selected by the i-th player.

Output

The output file should consist of only one integer, the maximum number of the existed werewolves.

Examples

Input 1

3
2 3 2

Output 1

2

题意:狼人杀游戏投票环节中,所有人都不可以投票给自己,狼人可以投票给好人,但不可以投给狼同伴,好人
可以投票给除自己以外任何人,现在知道有n个人参与游戏,以及每个人的投票情况,问这场游戏中最多有几个狼

题解:首先把问题看成一个图,因为每个人都不能投给自己,因此不存在自环,由于有n个点和n条边,且每个点的出度都是1,那么一定存在环和树,定义0表示这个人是好人,1表示这个人是狼人。
①首先考虑树的那一部分。定义dp[i][j]表示如果节点i是j(0/1)时,以i为根节点的子树最多有dp[i][j]个狼人。
对于根节点u,如果u是好人,那么与u相连的子节点v可以是好人也可以是狼人;如果u是狼人,那么与u
相连的子节点v一定是好人,就有状态转移方程:

dp[u][0]+=max(dp[v][0],dp[v][1]);

dp[u][1]+=dp[v][0];
②再考虑环的那一部分。由于树一定会与环相交于一点(因为每个点的出度为1),并且此前已经算出以这个点的dp[u][0]和dp[u][1],只要在每个环上进行一次DP:设环上有sz个点,编号0~sz-1,

F[i][0]=max(F[i-1][1],F[i-1][0])+dp[i][0];

F[i][1]=F[i-1][0]+dp[i][1];
对于编号为0,1的点特殊处理一下,分别枚举第0个点是0的情况和1的情况

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
const int MX = 5e5 + 5;
struct Edge {
    int v, nxt;
} E[MX * 2];
int head[MX], tot, IN[MX], d[MX], n, p[MX];
void add(int u, int v) {
    E[tot].v = v;
    E[tot].nxt = head[u];
    head[u] = tot++;
    IN[v]++;
}
void init() {
    memset(head, -1, sizeof(head));
    memset(IN, 0, sizeof(IN));
    tot = 0;
}
void top() {
    memset(p, 0, sizeof(p));
    queue<int>q;
    for(int i = 1; i <= n; i++) if(IN[i] == 1) {
          q.push(i);
          d[i] = 0;
     }
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v;
            IN[v]--;
            if(IN[v] == 1) {
                q.push(v);
                d[v] = d[u] + 1;
            }
        }
    }
    for(int i = 1; i <= n; i++) if(IN[i] <= 1) p[i] = 1;
}
int dp[MX][2]; //0是平民,1是狼
void dfs(int u, int fa) {
    dp[u][0] = 0; dp[u][1] = 1;
    if(d[u] == 0) return;
    for(int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == fa) continue;
        dfs(v, u);
        dp[u][0] += max(dp[v][0], dp[v][1]);
        dp[u][1] += dp[v][0];
    }
}

int path[MX];
void bfs(int u, int &sz) {
    queue<int>q;
    q.push(u);
    p[u] = 1;
    sz = 0;
    while(!q.empty()) {
        u = q.front(); q.pop();
        path[sz++] = u;
        for(int i = head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v;
            if(p[v]) continue;
            p[v] = 1;
            q.push(v);
            break;
        }
    }
}
int F[MX][2];
void solve() {
    for(int i = 1; i <= n; i++) {
        if(p[i]) continue;
        dp[i][0] = 0; dp[i][1] = 1;
        for(int j = head[i]; ~j; j = E[j].nxt) {
            int v = E[j].v;
            if(!p[v]) continue;
            dfs(v, i);
            dp[i][0] += max(dp[v][0], dp[v][1]);
            dp[i][1] += dp[v][0];
        }
    }
    memset(path, 0, sizeof(path));
    int ans = 0, sz;
    for(int i = 1; i <= n; i++) {
        if(p[i]) continue;
        bfs(i, sz);
        //第一个是0
        F[1][0] = dp[path[1]][0] + dp[path[0]][0];
        F[1][1] = dp[path[1]][1] + dp[path[0]][0];
        for(int i = 2; i < sz; i++) {
            F[i][0] = dp[path[i]][0] + max(F[i - 1][0], F[i - 1][1]);
            F[i][1] = dp[path[i]][1] + F[i - 1][0];
        }
        F[0][0] = max(F[sz - 1][0], F[sz - 1][1]);

        //第一个是1
        F[1][0] = dp[path[1]][0] + dp[path[0]][1];
        F[1][1] = 0;
        for(int i = 2; i < sz; i++) {
            F[i][0] = dp[path[i]][0] + max(F[i - 1][0], F[i - 1][1]);
            F[i][1] = dp[path[i]][1] + F[i - 1][0];
        }
        F[0][1] = F[sz - 1][0];
        ans += max(F[0][0], F[0][1]);
    }
    printf("%d\n", ans);
}
int main() {
   // freopen("in.txt", "r", stdin);
    while(~scanf("%d", &n)) {
        init();
        for(int i = 1; i <= n; i++) {
            int v;
            scanf("%d", &v);
            add(v, i);
            add(i, v);
        }
        top();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值