题面

传送门

思路

p到a

首先,本题中如果对于所有的$i$,连边$<i,p_i>$,那么可以得到一批环

那么这个题另外一点就是,可以变成连边$<i,p_{p_i}>$

我们分多种情况来讨论

情况1:啥也没变

就是啥也没变

情况2:全都变了

这时考虑奇环和偶环

对于一个奇环,全部变了以后,它还是一个环,但是不是同构的

对于一个偶环,全换了以后,它会变成两个环,分别是全部奇数节点和全部偶数节点

情况3:变了一部分

此时容易发现,变出去的部分应该会这样:

$<i,p_i>$变成$<i,p_{p_i}>$,则$p_i$变成环上外挂的一个节点

这样一直讨论下去可以得到,最终我们会得到一棵环套树,并且每一棵树都只有可能是一条链

对于每一条链我们这样考虑:

如果链形如这样:

那么肯定ans=0,因为这个长度为2的链没有2条环边配合它

如果是这样:

如果恰好相等,那么这条链只有一种情况

如果不相等,那么这条链会有两种情况:放在这一段环的最开始和最末尾

此时它对答案贡献一个乘2

a到p

讨论完以后,我们回到问题本身,发现我们拿到了的是$a$不是$p$

所以我们要求的实际上就是这个$<i,a_i>$图反推回去的方案数

那么,我们把环和环套树分开处理

显然环套树就是按照上面的讨论,乘1乘2或者乘0

对于所有长度相同的环,我们需要考虑它们两两合并的情况

此时可以用数学方法很快求出

具体一点来说,如果有$n$个长度为$L$的环,那么它们的贡献就是

$\sum_{t=0}^{\lfloor \frac{n}{2}\rfloor} L^n f(t2) C(n,2t) $

如果$n$是大于1的奇数还要再乘以2(因为此时可以像上面说的那样自我同构一下)

其中$f(2t)$表示的是$2t$个数互相配对的方案数

最后把所有长度的环的贡献乘起来,再乘上每个环套树的贡献,就是答案了

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define MOD 1000000007
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
ll f[200010],finv[200010],meth[200010];
ll qpow(ll a,ll b){
ll re=1;
while(b){
if(b&1) re=(re*a)%MOD;
a=a*a%MOD;b>>=1;
}
return re;
}
void init(){
ll i,len=200000;
f[0]=f[1]=finv[0]=finv[1]=1;
for(i=2;i<=len;i++) f[i]=f[i-1]*i%MOD;
finv[len]=qpow(f[len],MOD-2);
for(i=len;i>2;i--) finv[i-1]=finv[i]*i%MOD;
}
int n,a[200010],vis[200010],cir[200010],cntcir=0,in[200010],bst[200010],siz[200010];
ll ans=1;
vector<int>s;
vector<int>nd[200010];
bool cmp(int l,int r){
return siz[l]<siz[r];
}
ll C(ll x,ll y){
return f[x]*finv[y]%MOD*finv[x-y]%MOD;
}
int main(){
n=read();int i,j;ll tmp,c,cc;
init();
meth[0]=1;
for(i=1;i<=n;i++) meth[i]=C(i*2,i)*f[i]%MOD*qpow(qpow(2,MOD-2),i)%MOD; for(i=1;i<=n;i++) a[i]=read(),in[a[i]]++;
for(i=1;i<=n;i++){//取出环
j=i;
while(!vis[j]) vis[j]=i,j=a[j];
if(vis[j]^i) continue;
cntcir++;
while(!cir[j]){
cir[j]=cntcir,nd[cntcir].push_back(j),siz[cntcir]++,j=a[j];
}
}
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++){//判断环套树,以及外挂树是不是都是链
if(in[i]) continue;
j=i;
while(!cir[j]&&!vis[j]) j=a[j];
if(vis[j]){
puts("0");return 0;
}
bst[cir[j]]=1;tmp=cir[j];
j=i;c=0;
while(!cir[j]) cir[j]=tmp,c++,vis[j]=1,j=a[j];
vis[j]=c;
}
for(i=1;i<=cntcir;i++){//环套树处理
if(!bst[i]){s.push_back(i);continue;}
for(j=0;j<nd[i].size();j++) if(vis[nd[i][j]]) break;
tmp=j;
do{
c=j;cc=1;
j--;
(j+=(int)nd[i].size());
j%=(int)nd[i].size();
for(;!vis[nd[i][j]];j--,j=((j<0)?j+nd[i].size():j)) cc++;
if(vis[nd[i][c]]>cc){
puts("0");return 0;
}
if(vis[nd[i][c]]<cc) (ans*=2)%=MOD;
}while(tmp!=j);
}
sort(s.begin(),s.end(),cmp);
for(i=0;i<s.size();i+=c){
j=i;tmp=0;
while(siz[s[j]]==siz[s[i]]&&j<s.size()) j++;
c=j-i;
for(j=0;j<=c/2;j++){
(tmp+=C(c,2*j)*meth[j]%MOD*qpow(siz[s[i]],j)%MOD*qpow(2,(c-2*j)*(siz[s[i]]!=1)*(siz[s[i]]&1))%MOD)%=MOD;
}
ans=ans*tmp%MOD;
}
printf("%lld\n",ans);
}

[AGC008E] Next or Nextnext [环套树森林+结论讨论]的更多相关文章

  1. BZOJ4883: [Lydsy1705月赛]棋盘上的守卫(最小环套树森林&优化定向问题)

    4883: [Lydsy1705月赛]棋盘上的守卫 Time Limit: 3 Sec  Memory Limit: 256 MBSubmit: 475  Solved: 259[Submit][St ...

  2. BZOJ 4883 [Lydsy2017年5月月赛]棋盘上的守卫(最小生成环套树森林)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4883 [题目大意] 在一个n*m的棋盘上要放置若干个守卫. 对于n行来说,每行必须恰好 ...

  3. 【bzoj4883】[Lydsy2017年5月月赛]棋盘上的守卫 最小环套树森林

    题目描述 在一个n*m的棋盘上要放置若干个守卫.对于n行来说,每行必须恰好放置一个横向守卫:同理对于m列来说,每列必须恰好放置一个纵向守卫.每个位置放置守卫的代价是不一样的,且每个位置最多只能放置一个 ...

  4. BZOJ4886: [Lydsy1705月赛]叠塔游戏(环套树森林&贪心)

    4886: [Lydsy1705月赛]叠塔游戏 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 198  Solved: 76[Submit][Stat ...

  5. BZOJ1124 POI2008枪战Maf(环套树+贪心)

    每个点出度都为1,可以发现这张图其实是个环套树森林,树中儿子指向父亲,环上边同向. 首先自环肯定是没救的,先抬出去. 要使死亡人数最多的话,显然若一个点入度为0其不会死亡,而一个孤立的环至少会留下一个 ...

  6. BZOJ4883 棋盘上的守卫(环套树+最小生成树)

    容易想到网络流之类的东西,虽然范围看起来不太可做,不过这提供了一种想法,即将行列分别看做点.那么我们需要找一种连n+m条边的方案,使得可以从每条边中选一个点以覆盖所有点.显然每个点至少要连一条边.于是 ...

  7. BZOJ 1791 岛屿(环套树+单调队列DP)

    题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点 ...

  8. BZOJ 1040 骑士(环套树DP)

    如果m=n-1,显然这就是一个经典的树形dp. 现在是m=n,这是一个环套树森林,破掉这个环后,就成了一个树,那么这条破开的边连接的两个顶点不能同时选择.我们可以对这两个点进行两次树形DP根不选的情况 ...

  9. HDU 6251 Inkopolis(2017 CCPC-Final,I题,环套树 + 结论)

    题目链接 HDU 6251 题意 给出一个$N$个点$N$条边的无向图.然后给出$M$个操作,每个操作为$(x, y, z)$,表示把连接 $x$和$y$的边的颜色改成$z$. 求这张无向图中所有边的 ...

随机推荐

  1. poj_1284_Primitive root

    We say that integer x, 0 < x < p, is a primitive root modulo odd prime p if and only if the se ...

  2. Redis高可用

    redis高可用只要在于三个方面 主从复制 哨兵机制 集群机制 主从复制 主从复制作用: 1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式.2.故障恢复:当主节点出现问题时,可 ...

  3. LeetCode-177:第N高的薪水

    第N高的薪水与第二高的薪水,解题思路是一样的,只要对LeetCode-176的SQL做一下变形,便可以满足这题,详见:https://www.cnblogs.com/zouqf/p/10282392. ...

  4. elasticsearch-dsl聚合-1

    接续上篇,本篇介绍elasticsearch聚合查询,使用python库elasticsearch-dsl进行聚合查询操作. 7.3.聚合查询 高阶概念 Buckets(桶/集合):满足特定条件的文档 ...

  5. MySQL巧用FIND_IN_SET和GROUP_CONCAT函数减少Java代码量

    数据库表简介:物品表 `id` int(11)  '物品id,唯一标识', `name` varchar(255) '物品名称', `level` int(11) '物品类别等级,礼品包为最高级1,类 ...

  6. 图解HTTP总结(4)——返回结果的HTTP状态码

    HTTP状态码负责表示客户端HTTP请求的返回结果.标记服务器端的处理是否正常.通知出现的错误等工作. 状态码的类别 2XX 成功 200 OK 表示从客户端发来的请求在服务器端被正常处理了. 在响应 ...

  7. 笔记-mysql-管理及基础操作

    笔记-mysql使用-管理及基础操作 1.      简介 mysql是一个免费的关系型数据库,不过好像被oracle收购了.... 希望它继续免费. 1.1.    相关术语 数据库,表,列,行,冗 ...

  8. 直接选择排序&堆排序

    1.什么是直接选择排序? 直接选择排序(Straight Select Sort)是一种简单的排序方法,它的基本思想是:通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i ...

  9. 我的转行之路(电气转IT)------2018阿里校招面经

    博主本专业电气,今年3月下定决心转向互联网行业,本来想依仗自己比较自信的学习能力自学成才的,不过学了一段时间感觉还是需要一个人来指点,不仅仅是指点一些技术性的问题,更是需要有人来指点一下方向性的问题. ...

  10. 《Java程序员由笨鸟到菜鸟》

    <Java程序员由笨鸟到菜鸟> 在众多朋友的支持和鼓励下,<Java程序员由菜鸟到笨鸟>电子版终于和大家见面了.本电子书涵盖了从java基础到javaweb开放框架的大部分内容 ...