跳转至

B - Special Subsets

题意

1 以上 N 以下の整数すべてから成る集合を S とします。

fS から S への関数であり、f(1),f(2),\ldots,f(N) の値が f_1,f_2,\ldots,f_N として与えられます。

S の空でない部分集合 T であって、次の両方の条件を満たすものの個数を 998244353 で割った余りを求めてください。

  • 全ての a\in T について f(a)\in T である。
  • 全ての a,b\in T について a\neq b ならば f(a)\neq f(b) である。

解析

对于每一个 f(i)=f_i,我们都连一条有向边 (i,f_i)。那么题目要求的条件可以改写为:

  • i\in T,则所有能够通过 i 到达的点也属于 T
  • 所有点入度小于 2

因为每个点都恰好有一条出边,且该边连向的点也属于 T,故 T 的诱导子图恰好含有 |T| 条边。由于 T 中所有点入度之和等于 |T|,且所有点入度小于 2,由狄利克雷原理可知所有点的入度都恰为 1。故任何合法的 T 都是若干个环上所有点的集合。

由于原图中每个弱连通分量的边数与点数相等,故原图的每个弱连通分量都是一个基环树,包含且仅包含一个环。因此环的数目等于弱连通分量的数目,用并查集处理即可。

实现

#include <bits/stdc++.h>

const int maxn = 2e5 + 19, mod = 998244353;

int fa[maxn];
int find(int node){
    return fa[node] == node ? node : fa[node] = find(fa[node]);
}

int n, ans;

int main(){
    std::scanf("%d", &n), std::iota(fa + 1, fa + 1 + n, 1);
    for(int i = 1, f; i <= n; ++i){
        std::scanf("%d", &f);
        if(find(f) != find(i))
            fa[find(f)] = find(i);
    }
    ans = 1;
    for(int i = 1; i <= n; ++i)
        if(find(i) == i)
            ans = (ans + ans) % mod;
    ans = (ans + mod - 1) % mod;
    std::printf("%d\n", ans);
}

来源

AtCoder Regular Contest 114 B - Special Subsets

评论