拓扑排序求环的大小和每个环的元素(Link with Limit)

Link with Limit

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 415 Accepted Submission(s): 129

Problem Description
Link has a function f(x), where x and f(x) are both integers in [1,n].

Let fn(x)=f(fn−1(x)) and f1(x)=f(x), he define the power of a number x as:
g(x)=limn→+∞1n∑i=1nfi(x)

He wants to know whether x has the same power for all x∈[1,n].

Input
The input consists of multiple test cases.

The first line contains an integer T (1≤T≤100) – the number of test cases.

For each test case:

In the first line, there is an integer n (1≤n≤105).

In the second line, there are n integers, the i-th integer shows the value of f(i) (1≤f(i)≤n).

It is guaranteed that the sum of n over all test cases will not exceed 106.

Output
For each test case, output ‘YES’ if all x have the same power. Otherwise, output ‘NO’.

Sample Input
2
2
1 2
2
1 1

Sample Output
NO
YES

题意大致为求出每个环的平均点权(有向图,每个点只有一条出边)

拓扑排序依次去掉所有入度为零的点,剩下的点会组成环,易知此题环与环之间不相交,求一遍即可


#include <bits/stdc++.h>
typedef long long ll;
const ll N = 100100;
const ll mod = (ll)1e9 + 7;
using namespace std;
ll in[N];
ll a[N];
ll n;
ll ans[N], tot;
void solve() {
    scanf("%d", &n);
    for (ll i = 0; i <= n; i++) in[i] = 0;
    for (ll i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        in[a[i]]++;
    }
    queue<ll> que;
    for (ll i = 1; i <= n; i++)
        if (in[i] == 0) que.push(i);
    ll u;
    while (!que.empty()) {
        u = que.front();
        que.pop();
        in[a[u]]--;
        if (in[a[u]] == 0) que.push(a[u]);
    }
    ll p = -1, q = -1, flag = 0;
    ll sum, cnt;
    for (ll i = 1; i <= n; i++) {
        if (in[i] == 0) continue;
        sum = cnt = 0;
        for (ll j = i; in[j]; j = a[j]) {
            in[j] = 0;
            sum += j;
            cnt++;
        }
        if (p == -1 && q == -1) {
            p = sum, q = cnt;
        } else {
            if (p * cnt != q * sum) {
                flag = 1;
                break;
            }
        }
    }
    if (flag)
        printf("NO\n");
    else
        printf("YES\n");
}
int main() {
    ll t = 1;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值