传送门

考虑转化成图论问题,\(i\) 向 \(p_i\) 连边,那么合法方案一定是形成了若干个简单环或自环

考虑一个环内的情况:

  1. 如果 \(a_i=p_i\),那么 \(i\) 向 \(a_i\) 连边的图和原图相比不变
  2. 如果 \(a_i=p_{p_i}\),

    a. 环长为奇数且 \(>1\),那么 \(i\) 向 \(a_i\) 连边的图仍然是一个环(不同)

    b. 环长为偶数,那么 \(i\) 向 \(a_i\) 连边的图变成两个长度一样的环

    c. 环长为 \(1\),那么 \(i\) 向 \(a_i\) 连边的图仍然是一个自环
  3. 那么 \(i\) 向 \(a_i\) 连边的图成为一个基环内向森林

令 \(i\) 和 \(p_i\) 连边的图为原图,和 \(a_i\) 的为新图

现在已知 \(i\) 和 \(a_i\) 的边,求原图的方案数

考虑一个环上的一条链,它还原成原图的环的方法只能是沿着环的逆时针方向插空还原

因为新图的两个相邻点之间只能插入一个点

那么有显而易见的几种无解的情况:

  1. 对于一个不属于环上的点,如果有两个及以上的点同时指向它,那么肯定不能还原成原图的环
  2. 对于一个属于环上的点,如果有两个及以上的点同时指向它,那么肯定也不能还原成原图的环
  3. 如果环上一条链 \(a\) 和环的沿逆时针方向的另一条链的距离小于 \(a\) 的长度,那么无解

考虑计算答案:

  1. 对于新图的单个的环,把长度相同的一起 \(dp\),每次可以合并两个,或者奇数长度的同构等
  2. 对于基环内向树,如果环上一条链 \(a\) 和环的沿逆时针方向的另一条链的距离等于 \(a\) 的长度,那么只有一种方案,否则如果大于,就有两种,因为此时 \(a\) 的靠近环的点可以选择连上逆时针方向的一个点

把这些东西互不影响,乘法原理即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int maxn(1e5 + 5);
const int mod(1e9 + 7); inline void Inc(int &x, int y) {
x = x + y >= mod ? x + y - mod : x + y;
} inline void Dec(int &x, int y) {
x = x - y < 0 ? x - y + mod : x - y;
} inline int Add(int x, int y) {
return x + y >= mod ? x + y - mod : x + y;
} inline int Sub(int x, int y) {
return x - y < 0 ? x - y + mod : x - y;
} int n, cir[maxn], cnt, fa[maxn], vis[maxn], len, d[maxn], in[maxn];
int ans, f[maxn], a[maxn], chain[maxn], que[maxn << 1]; inline void GetCircle() {
int i;
for (i = 1; i <= len; ++i) if (d[que[i]] ^ 1) return;
cir[++cnt] = len;
for (i = 1; i <= len; ++i) vis[que[i]] = 2;
} void Dfs1(int u) {
int cur;
vis[u] = 1, in[u] = 1;
if (!vis[a[u]]) fa[a[u]] = u, Dfs1(a[u]);
else if (in[a[u]]) {
len = 0;
for (cur = u; ; cur = fa[cur]) {
que[++len] = cur, vis[cur] = 3;
if (cur == a[u]) break;
}
GetCircle();
}
in[u] = 0;
} void Dfs2(int u) {
chain[a[u]] = chain[u] + 1;
if (vis[a[u]] > 1) return;
Dfs2(a[u]);
} int Solve(int x) {
int cur, i, j, ret = 1;
que[len = 1] = x;
for (cur = a[x]; cur ^ x; cur = a[cur]) que[++len] = cur;
reverse(que + 1, que + len + 1), cur = len + len;
for (i = 1; i <= len; ++i) vis[que[i]] = 4, que[len + i] = que[i];
for (i = 1; i <= len; ++i)
if (chain[que[i]]) {
for (j = i + 1; j <= cur && !chain[que[j]]; ++j);
if (chain[que[i]] > j - i) puts("0"), exit(0);
if (chain[que[i]] < j - i) Inc(ret, ret);
i = j - 1;
}
return ret;
} int main() {
int i, j, k, ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; ++i) scanf("%d", &a[i]), ++d[a[i]];
for (i = 1; i <= n; ++i) if (!d[i]) Dfs1(i);
for (i = 1; i <= n; ++i) if (!vis[i]) Dfs1(i);
for (i = 1; i <= n; ++i)
if ((vis[i] > 1 && d[i] > 2) || (vis[i] == 1 && d[i] > 1)) return puts("0"), 0;
sort(cir + 1, cir + cnt + 1);
for (i = 1; i <= n; i = j) {
for (j = i; j <= n && cir[j] == cir[i]; ++j);
f[i - 1] = 1;
for (k = i; k < j; ++k) {
f[k] = f[k - 1];
if (cir[i] > 1 && (cir[i] & 1)) Inc(f[k], f[k - 1]);
if (k > i) Inc(f[k], (ll)f[k - 2] * (k - i) % mod * cir[i] % mod);
}
ret = (ll)ret * f[j - 1] % mod;
}
for (i = 1; i <= n; ++i) if (vis[i] == 1) vis[i] = 0;
for (i = 1; i <= n; ++i) if (!d[i]) Dfs2(i);
for (i = 1; i <= n; ++i) if (vis[i] == 3) ret = (ll)ret * Solve(i) % mod;
printf("%d\n", ret);
return 0;
}

AGC008E:Next or Nextnext的更多相关文章

  1. windows下安装ubuntu15.04

    本文主要介绍windows下安装ubuntu15.04,对与其他的版本也是适用的.现在要讲的是一种最简单ubuntu的安装方式. 1软件下载 1.磁盘分区工具DiskGenius 2.启动项修改工具E ...

  2. java web 开发三剑客 -------电子书

    Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知In ...

  3. 所有selenium相关的库

    通过爬虫 获取 官方文档库 如果想获取 相应的库 修改对应配置即可 代码如下 from urllib.parse import urljoin import requests from lxml im ...

  4. [AGC008E] Next or Nextnext [环套树森林+结论讨论]

    题面 传送门 思路 p到a 首先,本题中如果对于所有的$i$,连边$<i,p_i>$,那么可以得到一批环 那么这个题另外一点就是,可以变成连边$<i,p_{p_i}>$ 我们分 ...

  5. AGC008E Next or Nextnext(组合计数,神奇思路)

    神仙题. 排列计数,一种常见的做法是 \(i\) 向 \(p_i\) 连边. 然而这里这个就逼迫我们只能从 \(i\) 向 \(a_i\) 连边. 不过没关系,考虑从 \(i\) 向 \(p_i\) ...

  6. @atcoder - AGC008E@ Next or Nextnext

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个长度为 N 的序列 a,问有多少排列 p,满足对于每一个 ...

  7. Spring Cloud(2):搭建Eureka

    Eureka Server的搭建: 使用IDEA工具 File->New Project->Spring Initializr->next Next Next->Next创建即 ...

  8. In-Memory:内存数据库

    在逝去的2016后半年,由于项目需要支持数据的快速更新和多用户的高并发负载,我试水SQL Server 2016的In-Memory OLTP,创建内存数据库实现项目的负载需求,现在项目接近尾声,系统 ...

  9. 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代

    2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...

随机推荐

  1. 【JS深入学习】——事件代理/事件委托

    事件代理/事件委托(event delegation) 需求一:当一个div内部有多个事件发生,给每个元素逐个添加事件十分麻烦... 需求二:在项目中我们常常需要动态的添加元素,不可避免的需要为那些未 ...

  2. 在Ubuntu中增加root用户登录

    一:增加root用户登录 1.打开终端,输入:sudo gedit /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf 2.在弹出的编辑框里输入:gree ...

  3. c malloc分配内存

    php中的内存分配有用类似emalloc这样的函数,emalloc实际上是C语言中的malloc的一层封装,php启动后,会向OS申请一块内存,可以理解为内存池,以后的php分配内存都是在这块内存池中 ...

  4. Python shutil模块(目录和文件操作)

    import shutil #导入shutil模块 copyfileobj方法 将类文件对象fsrc的内容复制到类文件对象fdst shutil.copyfileobj(fsrc, fdst[, le ...

  5. AngularJS Directive 命名规则

    使用规则 在HTML代码中,使用的是连接符的形式,下面我们对比看看命名和使用的对应字符串: 命名 使用 people people peopleList people-list peopleListA ...

  6. html的css选择器

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. Java跨语言调用,使用JNA访问Java外部接口

    1. JNA简单介绍 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即 ...

  8. Kafka—性能逆天的存在

    0.引言 Kafka是LinkedIn开源出来的一款消息服务器,用Scala语言实现:这货的性能是百万级的QPS(估计是挂载了多块磁盘),我随便写个测试程序就是十万级. 1.Kafka基本概念 在Ka ...

  9. java学习-MD5消息摘要算法

    md5 属于hash算法一类,是不可逆的消息摘要算法.与对称加密和非对称加密算法不一样,不需要加密密钥. 注意: md5不是加密算法,只是将数据进行散列计算后生成一个唯一值的算法,没有加密密钥也没有解 ...

  10. Impala 使用的端口

    下表中列出了 Impala 是用的 TCP 端口.在部署 Impala 之前,请确保每个系统上这些端口都是打开的. 组件 服务 端口 访问需求 备注 Impala Daemon Impala 守护进程 ...