@atcoder - [email protected] Next or Nextnext

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一个长度为 N 的序列 a,问有多少排列 p,满足对于每一个 i,都有 \(a_i = p_i\) 或 \(a_i = p_{p_i}\) 成立。

原题传送门。

@[email protected]

为了更直观地理解问题,不妨建个图:连有向边 \(i -> p_i\)。
对于任意序列 a,这样建出的图是一个基环树森林;对于排列 p,这样建出的图是若干个环。

当 \(a_i = p_i\) 或 \(a_i = p_{p_i}\) 成立时,相当于我们把排列 p 对应的图的某个边 \(i -> p_i\) 替换成 \(i -> p_{p_i}\),最终得到序列 a 对应的图。

如果将某一个奇环的边全部替换,将得到另一个大小相同的奇环。
如果将某一个偶环的边全部替换,将得到两个大小为原先一半的环。
如果将某一个环的边不完全替换(不包括不替换的情况),一定得到一个基环树。

那么我们可以将 a 序列中相同大小的环放在一起计数,把 a 序列中每一个基环树进行计数。
环的情况一定是全部替换/全部不替换,上面的讨论已经包含了所有情况。

考虑基环树。首先你自己手玩一下,发现基环树如果要合法,一定是一个环 + 某些环上的点延伸出去恰好一条链。
其实比较容易理解。替换后每个点的入度最多为 2,且入度为 2 的一定是环上的点。

理论上延伸出去一条链的环上的点会贡献 2 倍方案数:可能延伸出去的链是被替换的;可能环上的入边是被替换的。但是有不合法的情况。
不妨记延伸出去链长为 x,顺着入边走找到的最近的有链延伸出去的点距离为 y。
当 x < y 时,延伸出去的链才可能是被替换的;当 x <= y 时,环上的入边才可能是被替换的。
这个自己手玩一下就能发现了。

@accepted [email protected]

#include <cstdio>
#include <algorithm>
using namespace std;

#define ILLEGAL puts("0"), exit(0)

const int MAXN = 100000;
const int MOD = int(1E9) + 7;
const int INV2 = (MOD + 1) / 2;

int pow_mod(int b, int p) {
    int ret = 1;
    for(int i=p;i;i>>=1,b=1LL*b*b%MOD)
        if( i & 1 ) ret = 1LL*ret*b%MOD;
    return ret;
}

int fct[MAXN + 5], ifct[MAXN + 5], f[MAXN + 5];
int comb(int n, int m) {
    return 1LL*fct[n]*ifct[m]%MOD*ifct[n-m]%MOD;
}
void init() {
    fct[0] = 1;
    for(int i=1;i<=MAXN;i++)
        fct[i] = 1LL*fct[i-1]*i%MOD;
    ifct[MAXN] = pow_mod(fct[MAXN], MOD - 2);
    for(int i=MAXN-1;i>=0;i--)
        ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
    f[0] = 1;
    for(int i=2;i<=MAXN;i+=2)
        f[i] = 1LL*comb(i, 2)*f[i-2]%MOD*pow_mod(i/2, MOD-2)%MOD;
}

int a[MAXN + 5], N;
bool tag[MAXN + 5], vis[MAXN + 5];
int cnt[MAXN + 5], b[MAXN + 5], ind[MAXN + 5];

int main() {
    init(), scanf("%d", &N);
    for(int i=1;i<=N;i++)
        scanf("%d", &a[i]), ind[a[i]]++;
    for(int i=1;i<=N;i++)
        if( ind[i] >= 3 ) ILLEGAL;
    for(int i=1;i<=N;i++) {
        if( ind[i] == 2 ) {
            if( tag[i] ) continue;
            int p = i;
            while( !vis[p] )
                vis[p] = true, p = a[p];
            if( p != i ) ILLEGAL;
            p = i;
            do {
                tag[p] = true, p = a[p];
            }while( p != i );
        }
    }
    for(int i=1;i<=N;i++) {
        if( ind[i] == 0 ) {
            int t = 0, p = i;
            while( !tag[p] )
                vis[p] = true, t++, p = a[p];
            b[p] = t;
        }
    }
    int ans = 1;
    for(int i=1;i<=N;i++) {
        if( b[i] ) {
            int t = 0, p = i;
            do {
                t++, p = a[p];
            }while( !b[p] );
            if( t < b[p] ) ILLEGAL;
            else if( t > b[p] ) ans = 2LL*ans%MOD;
        }
    }
    for(int i=1;i<=N;i++) {
        if( !vis[i] ) {
            int t = 0, p = i;
            do {
                vis[p] = true, t++, p = a[p];
            }while( !vis[p] );
            cnt[t]++;
        }
    }
    for(int i=1;i<=N;i++) {
        int del = 0;
        for(int j=0;j<=cnt[i];j+=2) {
            int tmp = 1;
            tmp = 1LL*tmp*comb(cnt[i], j)%MOD*f[j]%MOD*pow_mod(i, j/2)%MOD;
            if( (i & 1) && i != 1 ) {
                tmp = 1LL*tmp*pow_mod(2, cnt[i] - j)%MOD;
            }
            del = (del + tmp) % MOD;
        }
        ans = 1LL*ans*del%MOD;
    }
    printf("%d\n", ans);
}

@[email protected]

其实细节比较多。不过这道题最奇妙的是数形结合利用建图简化思维。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12416577.html

时间: 2024-08-01 23:06:20

@atcoder - [email protected] Next or Nextnext的相关文章

@atcoder - [email&#160;protected] Contest with Drinks Hard

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定序列 T1, T2, ... TN,你可以从中选择一些 Ti,可以选择 0 个(即不选). 定义你选择的权值 = (满足 T[L...R] 都被选择的区间 [L, R] 的数量)-(你选择的 Ti 之和),你希望这个权值尽量大. 现在有 M 次询问,每次询问假如将 T[Pi] 修改

@atcoder - [email&#160;protected] Colorful Tree

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 题面明天起来补... @[email protected] 跟颜色有关,虽然题目中说是要"修改"实际上每一个询问之间是独立的,即修改不会有时效性. 所以不难想到可以使用树上莫队. 首先将边的颜色与边权下放至点.然后莫队时,统计出区间内每个点的点权(就是下放的边权)和 S.区间

@atcoder - [email&#160;protected] Gachapon

目录 @[email protected] @solution - [email protected] @accepted code - [email protected] @solution - [email protected] @accepted code - [email protected] @[email protected] @[email protected] 一个随机数发生器会生成 N 种数. 第 i 种数有参数 Ai,记 SA = ∑Ai,随机数发生器会有 Ai/SA 的概率

@atcoder - [email&#160;protected] RNG and XOR

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个值域在 [0, 2^N) 的随机数生成器,给定参数 A[0...2^N-1]. 该生成器有 \(\frac{A_i}{\sum A}\) 的概率生成 i,每次生成都是独立的. 现在有一个 X,初始为 0.每次操作生成一个随机数 v 并将 X 异或 v. 对于每一个 i ∈ [0

@atcoder - [email&#160;protected] Simple Subsequence Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定由若干长度 <= N 的 01 字符串组成的集合 S.请找到长度最长的串 t(如果有多个选字典序最小的),使得存在 >= K 个 S 中的字符串,使得 t 是这些字符串的子序列. 原题题面. @[email protected] 先看看怎么检验串 t 是否为某个串 s 的子序列:

@atcoder - [email&#160;protected] Two Trees

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定两棵树 A, B.现你需要构造一组值 (X1, X2, ..., XN)(两棵树编号相同的点对应权值相同),使得两棵树内任意子树的权值和的绝对值为 1. 无解输出 IMPOSSIBLE. 原题链接. @[email protected] 由于权值和为 1,可以推算出每个结点的奇偶性

@atcoder - CODE FESTIVAL 2017 Final - [email&#160;protected] Tree MST

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定 N 个点,第 i 点有一个点权 Xi,再给定一棵边带权的树,第 i 条 (Ai, Bi) 边权为 Ci. 构建一个完全图,完全图中边 (i, j) 的边权为 dist(i, j) + Xi + Xj,其中 dist(i, j) 是点 i 与点 j 在树上的距离. 求该完全图的最小

$*和[email&#160;protected]之间区别代码分析

#!/bin/bash set 'apple pie' pears peaches for i in $*           /*单引号被去掉,循环单个字符输出*/ do echo $i done [[email protected] Ex_14.02-14.31]# sh 14-14-1 apple pie pears peaches -------------------------------------------------------------- #!/bin/bash set

[email&#160;protected]一个高效的配置管理工具--Ansible configure management--翻译(六)

无书面许可请勿转载 高级playbook Finding files with variables All modules can take variables as part of their arguments by dereferencing them with {{ and }} . You can use this to load a particular file based on a variable. For example, you might want to select a