神仙题。

排列计数,一种常见的做法是 \(i\) 向 \(p_i\) 连边。

然而这里这个就逼迫我们只能从 \(i\) 向 \(a_i\) 连边。

不过没关系,考虑从 \(i\) 向 \(p_i\) 连边的图(为方便叫 \(G_1\))和从 \(i\) 向 \(a_i\) 连边的图(为方便叫 \(G_2\))的区别。

首先 \(G_1\) 中每个点入度和出度都是 \(1\),所以是一堆环构成的。

考虑一个环:(下面建议画图,懒的建议看 litble 学姐的博客,自己不敢直接把图拿过来)

  • 如果上面所有点都满足 \(p_i=a_i\),那么这个环在 \(G_2\) 中也出现了,长得一样。
  • 如果上面所有点都满足 \(p_{p_i}=a_i\),且这个环长度为奇数,那么有一个相同长度的环在 \(G_2\) 中出现了,但是长得不一样。
  • 如果上面所有点都满足 \(p_{p_i}=a_i\),且这个环长度为偶数,那么有两个长度都为这个环长度的一半的环在 \(G_2\) 中出现了。
  • 如果上面的点又有满足 \(p_i=a_i\) 的,又有满足 \(p_{p_i}=a_i\) 的,那么会有一个点数相同的基环内向树在 \(G_2\) 中出现。(这种情况比较复杂,等会再说)

现在知道了 \(G_2\) ,问 \(G_1\) 的方案数。

环和基环树独立。先看环。

令长度为 \(i\) 的环有 \(cnt_i\) 个。每个环要么是单独在 \(G_1\) 中出现,要么是与另一个环拼成一个大环在 \(G_1\) 中出现。

枚举与别的环一起拼的环的个数 \(2j\),那么把下面这些全步乘起来:

  • \(j\ne 0\) 时,先选出这些环,\(\binom{cnt_i}{2j}\);
  • \(j\ne 0\) 时,然后想象成是个二分图,枚举左边的环是哪些,再把右边的环分给左边的环。注意实际上没有顺序,所以要再除掉一堆 \(2\)。\(\frac{\binom{2j}{j}j!}{2^j}\);
  • \(j\ne 0\) 时,每对环有 \(i\) 种拼法,\(i^j\);
  • \(i\) 为奇数且 \(i\ne 1\) 时,没有与别的环拼起来的环可以以两种形态在 \(G_1\) 中出现,\(2^{cnt_i-2j}\)。

再看基环树。基环树之间也相互独立。

挂在基环树上的一堆链要压到环上。图的话,建议继续看学姐的博客。

抓比较重要的几点来说:1、每条链不能重叠,所以压链的话不能超过上一个有链的点。2、如果不考虑 1 中的限制,每条链有两种压法,上面的第一个点在 \(G_1\) 中直接连向 \(u\) 和两步连向 \(u\)。

所以说,每条链的压法只有 \(0,1,2\),且互相独立。直接乘起来。

一些无解情况也可以很简单判了:在环上的点入度 \(\le 2\),不在环上的点入度 \(\le 1\)。联想一下基环树的方案数算法应该很好理解。

时间复杂度 \(O(n\log n)\),如果有闲心也可以做到 \(O(n)\)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010,mod=1000000007,inv2=500000004;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
int x=0,f=0;char ch=getchar();
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,a[maxn],deg[maxn],ans=1,stk[maxn],tp,q[maxn],h,r,len[maxn],seq[maxn],tot,cnt[maxn];
int fac[maxn],inv[maxn],invfac[maxn];
bool vis[maxn],ins[maxn],cyc[maxn];
void dfs(int u){
if(vis[u]){
if(ins[u]){
ROF(i,tp,1){
cyc[stk[i]]=true;
if(stk[i]==u) break;
}
}
return;
}
vis[u]=ins[u]=true;
stk[++tp]=u;
dfs(a[u]);
ins[u]=false;
}
bool dfs2(int u){
if(vis[u]) return true;
vis[u]=true;
seq[++tot]=len[u];
return dfs2(a[u]) && !len[u];
}
inline int C(int n,int m){
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
inline int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
return ans;
}
int main(){
n=read();
FOR(i,1,n) a[i]=read(),deg[a[i]]++;
FOR(i,1,n) dfs(i);
FOR(i,1,n) if(cyc[i] && deg[i]>=3|| !cyc[i] && deg[i]>=2) return puts("0"),0;
h=1;r=0;
FOR(i,1,n) if(!deg[i]) q[++r]=i;
while(h<=r){
int u=q[h++];
len[a[u]]=len[u]+1;
if(!cyc[a[u]]) q[++r]=a[u];
}
MEM(vis,0);
FOR(i,1,n) if(cyc[i] && !vis[i]){
tot=0;
if(dfs2(i)) cnt[tot]++;
else{
int pre=0;
FOR(j,1,tot) if(seq[j]){
if(pre){
int at=j-seq[j];
if(at<pre) return puts("0"),0;
if(at>pre && tot>=2) ans=2*ans%mod;
}
pre=j;
}
FOR(j,1,tot) if(seq[j]){
int at=j-seq[j]+tot;
if(at<pre) return puts("0"),0;
if(at>pre && tot>=2) ans=2*ans%mod;
break;
}
}
}
fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
FOR(i,2,n){
fac[i]=1ll*fac[i-1]*i%mod;
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
}
FOR(i,1,n) if(cnt[i]){
int s=0;
FOR(j,0,cnt[i]/2){
int x=1ll*C(cnt[i],2*j)*C(2*j,j)%mod*fac[j]%mod;
if(j) x=1ll*x*qpow(inv2,j)%mod*qpow(i,j)%mod;
if(i%2==1 && i!=1) x=1ll*x*qpow(2,cnt[i]-2*j)%mod;
s=(s+x)%mod;
}
ans=1ll*ans*s%mod;
}
printf("%d\n",ans);
}

AGC008E Next or Nextnext(组合计数,神奇思路)的更多相关文章

  1. [总结]数论和组合计数类数学相关(定理&证明&板子)

    0 写在前面 0.0 前言 由于我太菜了,导致一些东西一学就忘,特开此文来记录下最让我头痛的数学相关问题. 一些引用的文字都注释了原文链接,若侵犯了您的权益,敬请告知:若文章中出现错误,也烦请告知. ...

  2. bzoj 2281 [Sdoi2011]黑白棋(博弈+组合计数)

    黑白棋(game) [问题描述] 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色 ...

  3. WC集训DAY2笔记 组合计数 part.1

    目录 WC集训DAY2笔记 组合计数 part.1 基础知识 组合恒等式 错排数 卡特兰数 斯特林数 伯努利数 贝尔数 调和级数 后记 补完了几天前写的东西 WC集训DAY2笔记 组合计数 part. ...

  4. BZOJ1079 [SCOI2008]着色方案[组合计数DP]

    $有a_{1}个1,a_{2}个2,...,a_{n}个n(n<=15,a_{n}<=5),求排成一列相邻位不相同的方案数.$ 关于这题的教训记录: 学会对于复杂的影响分开计,善于发现整体 ...

  5. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  6. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [FFT 组合计数 容斥原理]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  7. 【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)

    [HNOI2019]多边形(模拟,组合计数) 题面 洛谷 题解 突然特别想骂人,本来我考场现切了的,结果WA了几个点,刚刚拿代码一看有个地方忘记取模了. 首先发现终止态一定是所有点都向\(n\)连边( ...

  8. 【BZOJ5323】[JXOI2018]游戏(组合计数,线性筛)

    [BZOJ5323][JXOI2018]游戏(组合计数,线性筛) 题面 BZOJ 洛谷 题解 显然要考虑的位置只有那些在\([l,r]\)中不存在任意一个约数的数. 假设这样的数有\(x\)个,那么剩 ...

  9. 【BZOJ5305】[HAOI2018]苹果树(组合计数)

    [BZOJ5305][HAOI2018]苹果树(组合计数) 题面 BZOJ 洛谷 题解 考虑对于每条边计算贡献.每条边的贡献是\(size*(n-size)\). 对于某个点\(u\),如果它有一棵大 ...

  10. 【BZOJ3142】[HNOI2013]数列(组合计数)

    [BZOJ3142][HNOI2013]数列(组合计数) 题面 BZOJ 洛谷 题解 唯一考虑的就是把一段值给分配给\(k-1\)天,假设这\(k-1\)天分配好了,第\(i\)天是\(a_i\),假 ...

随机推荐

  1. mathematica练习程序(曲线的曲率与挠率)

    曲线的曲率k表示曲线的弯曲程度. 计算公式: 曲线的挠率tao表示曲率平面的扭曲程度,平面曲线挠率为0. 计算公式: 这里r代表曲线方程,比如有如下曲线方程:r={a*cos(t),a*sin(t), ...

  2. vue的基础概念和语法01

    vue的特点和web开发中的常见高级功能 解耦视图和数据 可复用的组件 前端路由技术 状态管理 虚拟DOM 数据响应式 不是所有元素操作都Vue都会监听并实现数据响应式 //push方法:追加 thi ...

  3. COMP2521: Assignment

    COMP2521: Assignment 2Social Network AnalysisA notice on the class web page will be posted after eac ...

  4. linux服务器上配置进行kaggle比赛的深度学习tensorflow keras环境详细教程

    本文首发于个人博客https://kezunlin.me/post/6b505d27/,欢迎阅读最新内容! full guide tutorial to install and configure d ...

  5. win7系统下安装Ubuntu18.04组成双系统

    最近在闲鱼上花了350大洋淘到了一台tinkpad sl510,这大概是一台发布于2009年的一台电脑了吧,处理器是酷睿二t440,2Gddr3的显卡,让我有点意外的是这台电脑的硬盘是7200转的32 ...

  6. java高并发系列 - 第4天:JMM相关的一些概念

    JMM(java内存模型),由于并发程序要比串行程序复杂很多,其中一个重要原因是并发程序中数据访问一致性和安全性将会受到严重挑战.如何保证一个线程可以看到正确的数据呢?这个问题看起来很白痴.对于串行程 ...

  7. Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application-笔记

    参考 Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application http://www.vnfan.com/helinbin/d/745b1e040c9b4f6 ...

  8. Python分页爬取数据的分析

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 向右奔跑 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  9. 2-2-for循环

    重复执行某些代码 每次执行的时候有个数字在变化 常用格式 <script> for(var i=0; i<3; i++){alert(i); } </script> 1) ...

  10. flex弹性布局没有生效

    display: -webkit-flex; /* 新版本语法: Chrome 21+ */ display: -webkit-box; /* 老版本语法: Safari, iOS, Android ...