题目链接:http://nyoj.top/problem/1252

  • 内存限制:64MB 时间限制:2000ms

题目描述

某帝国拥有着N 种被称作“世界之威”的新型武器。现在为了国家的经济发展,它需要很多资金,为此,此帝国总统OBM准备把一些武器卖给其它国家。

此帝国总统OBM知道,这N种武器之间可能会相互受限制的。例如,第3种武器会打败第1种武器,第4种武器会打败第3种武器等等。他希望所有被卖出的武器中都至少有一种限制它的武器在他手中,目的是此帝国能制约住其它国家,这样就可以保持对世界的霸权地位。

但总统OBM头脑有点笨,他想知道他最多可以卖出多少种武器,你愿意帮助他吗?

(你可以告诉电脑,但不告诉他喽。)

输入描述

第一行:K 表示有多少组测试数据。
接下来对每组测试数据:
第1行: N 表示有多少种新型武器
第2~N+1行:A1  A2 ……AN 表示第i 种武器能限制住第Ai 种武器
1≤K≤10 0≤N≤1000000 1≤Ai≤N

输出描述

对于每组测试数据,输出占一行:最多可以卖出的武器种类数

样例输入

1
7
2
3
1
3
6
5
4

样例输出

3

解题思路

题意:n个武器,希望被卖出的武器中都至少有一种限制它的武器在手中,求最多能卖出去多少武器。
思路:图的遍历。首先是建图,如果u克制v,就连一条有向边uv。因为一种武器只能克制一种武器,所以图的结构可能出现链和环,还有可能多条边汇聚在一个点,不可能出现一个点引出多条边。首先我们找到所有入度为0的点,这些武器肯定是不能卖的,因为无法克制它们。如果u入度为0,我们就从u开始遍历,一定能得到一条链,这条链的贡献就是链长度/2。把所有入度为0的点处理完后,所有的链就处理完了,剩下的就是环。从每个环的任意一个点开始遍历,得到环的长度,这个环的贡献就是环长度/2。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int deg[1000005], pre[1000005], vis[1000005];
int main() {
    int t, n, x, ans;
    scanf("%d", &t);
    while (t--) {
        ans = 0;
        scanf("%d", &n);
        memset(deg, 0, sizeof(deg));
        memset(pre, 0, sizeof(pre));
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x);
            pre[i] = x;
            deg[x]++;
        }
        for (int i = 1; i <= n; i++) {
            if (!deg[i]) {
                int cnt = 0, a = i;
                while (a && !vis[a]) {
                    vis[a] = 1;
                    a = pre[a];
                    cnt++;
                }
                ans += cnt >> 1;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                int cnt = 0, a = i;
                while (!vis[a]) {
                    vis[a] = 1;
                    a = pre[a];
                    cnt++;
                }
                ans += cnt >> 1;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}