计数套路题?但是我连套路都不会,,,

拿到这道题我一脸蒙彼,,,感谢@poorpool 大佬的博客的指点

先将第\(i\)位上的数字\(p_i\)向\(i\)连无向边,然后构成了一个有若干环组成的无向图,可以知道某个点包含它的有且仅有一个环,因为所有点度数都为2(自环的点度数也是2吧qwq)

那么我们的最终目标就是把这个图转换成有\(n\)个自环的图,相当于把排列排好顺序

考虑对于一个\(m\)个点的环,把它变成\(m\)个自环至少需要\(m-1\)步(相当于排列\((2,3...m,1)\)变成\((1,2,3...m)\),这至少需要\(m-1\)次交换)

设把一个\(m\)个点的环变成\(m\)个自环的方案数为\(f_m\).因为一次操作可以把一个环拆成两个环,设拆出来两个环大小为\(x,y\),那么拆环的方案数\(g(x,y)\)为$$g(x,y)=\begin{cases}x,\quad (x=y)\x+y, \quad others\end{cases} $$

就是对于每一个点,有两个点使得这一个点和那两个点中的一个进行交换操作可以分出大小为\(x,y\)的环,因为点对会被计算2次,所以方案为\(\frac{(x+y)*2}{2}=x+y\),如果\(x=y\),那么那两个点是同一点,所以方案要除以2

每个环之间的操作是不会相互影响的,所以可以交错进行操作,两个环贡献答案给一个大环时,两个环的操作交错所构成的排列数就是可重集的排列数(总元素个数阶乘除以每一中元素个数阶乘).综上,我们可以知道$$f_m=\sum_{i=1}^{\lfloor\frac{m}{2}\rfloor} g(i,m-i)f_if_{m-i}\frac{(m-2)!}{(i-1)!(m-i-1)!}$$

我们可以找出\(k\)个大小为\(a_1,a_2...a_k\)的环

把所有环的答案合并,同样每个环之间的操作是不会相互影响的,所以方案数要乘上一个可重集的排列数,可以得到$$ans=\prod_{i=1}^{k} f_{a_i}*\frac{(n-k)!}{\prod_{i=1}^{k}(a_i-1)!}$$

(注意边界,\(f_1=f_2=1\))

然而求\(f\)要\(O(n)\)的时间,总复杂度为\(O(nk)\)

考虑优化此算法,通过打表找规律可以发现\(f_i=i^{i-2}\)

然后求\(f\)只要要\(O(logn)\)了,爽!

然后就可以偷税的写代码了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register using namespace std;
const LL mod=1000000009;
il LL rd()
{
re LL x=0,w=1;re char ch;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int to[200010],nt[200010],hd[100010],tot=1;
int vis[200010],a[100010],tt;
LL jc[100010],cj[100010]; //不要问我为什么把阶乘数组的逆元写成cj(逃
bool v[100010];
il void add(int x,int y)
{
++tot;to[tot]=y;nt[tot]=hd[x];hd[x]=tot;
++tot;to[tot]=x;nt[tot]=hd[y];hd[y]=tot;
}
il void dfs(int x,int o,int h)
{
if(!v[x]) ++a[o];v[x]=true;
for(int i=hd[x];i;i=nt[i])
{
if(vis[i]==h) continue;
vis[i]=vis[i^1]=h;
dfs(to[i],o,h);
}
}
il LL ksm(LL a,LL b)
{
if(b<=0) return 1; //i=1时,i-2会为-1 qwq
LL an=1;
while(b)
{
if(b&1) an=(an*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return an;
} int main()
{
jc[0]=cj[0]=1;
for(int i=1;i<=100000;i++) jc[i]=(jc[i-1]*i)%mod,cj[i]=ksm(jc[i],mod-2);
int T=rd();
for(int h=1;h<=T;h++)
{
tot=1;tt=0;
memset(a,0,sizeof(a));
memset(v,0,sizeof(v));
memset(hd,0,sizeof(hd));
int n=rd();
for(int i=1;i<=n;i++) add(i,rd());
for(int i=1;i<=n;i++)
if(!v[i]) dfs(i,++tt,h);
LL ans=jc[n-tt];
for(int i=1;i<=tt;i++) ans=((ans*ksm(a[i],a[i]-2))%mod*cj[a[i]-1])%mod;
printf("%lld\n",ans);
}
return 0;
}

luogu P4778 Counting swaps的更多相关文章

  1. 洛谷P4778 Counting swaps 数论

    正解:数论 解题报告: 传送门! 首先考虑最终的状态是固定的,所以可以知道初始状态的每个数要去哪个地方,就可以考虑给每个数$a$连一条边,指向一个数$b$,表示$a$最后要移至$b$所在的位置 显然每 ...

  2. P4778 Counting Swaps 题解

    第一道 A 掉的严格意义上的组合计数题,特来纪念一发. 第一次真正接触到这种类型的题,给人感觉好像思维得很发散才行-- 对于一个排列 \(p_1,p_2,\dots,p_n\),对于每个 \(i\) ...

  3. CH3602 Counting Swaps

    题意 3602 Counting Swaps 0x30「数学知识」例题 背景 https://ipsc.ksp.sk/2016/real/problems/c.html Just like yeste ...

  4. Counting swaps

    Counting swaps 给你一个1-n的排列,问用最少的交换次数使之变为递增排列的方案数\(mod\ 10^9+7\),1 ≤ n ≤ 10^5. 解 显然最少的交换次数不定,还得需要找到最小交 ...

  5. LFYZOJ 104 Counting Swaps

    题解 #include <iostream> #include <cstdio> #include <algorithm> #include <cmath&g ...

  6. lfyzoj104 Counting Swaps

    问题描述 给定你一个 \(1 \sim n\) 的排列 \(\{p_i\}\),可进行若干次操作,每次选择两个整数 \(x,y\),交换 \(p_x,p_y\). 请你告诉穰子,用最少的操作次数将给定 ...

  7. luoguP4778 Counting swaps

    题目链接 题解 首先,对于每个\(i\)向\(a[i]\)连边. 这样会连出许多独立的环. 可以证明,交换操作不会跨越环. 每个环内的点到最终状态最少交换步数是 \(环的大小-1\) 那么设\(f[i ...

  8. 0x36 组合计数

    组合计算的性质: C(n,m)= m! / (n!(m-n)!) C(n,m)=C(m-n,m); C(n,m)=C(n,m-1)+C(n-1,m-1); 二项式定理:(a+b)^n=sigema(k ...

  9. 线段树合并 || 树状数组 || 离散化 || BZOJ 4756: [Usaco2017 Jan]Promotion Counting || Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

    题面:P3605 [USACO17JAN]Promotion Counting晋升者计数 题解:这是一道万能题,树状数组 || 主席树 || 线段树合并 || 莫队套分块 || 线段树 都可以写..记 ...

随机推荐

  1. jvm学习三:自定义ClassLoader

    第一节我们说过一句话:所有的Java虚拟机实现必须在每个类或接口被Java程序“ 首次主动使用”时才初始化他们 但类加载器却不是这样:类加载器不需要等到某个类“被首次主动使用”时才加载类 两句话的区别 ...

  2. js數據類型

    js的數據類型有:字符串.數字.布爾型.數組.undfined.null: js擁有動態類型,同樣的變量可以賦值多個類型: 變量賦值可以聲明后賦值,或者聲明時賦值: 字符串: 字符串用單引號或者雙引號 ...

  3. BZOJ4650 NOI2016优秀的拆分(后缀数组)

    显然只要求出以每个位置开始的AA串数量就可以了,将其和反串同位置的结果乘一下,加起来就是答案.考虑对每种长度的字符串计数.若当前考虑的A串长度为x,我们每隔x个字符设一个关键点,求出相邻两关键点的后缀 ...

  4. Wordpress 之删除 RSS 功能 的"文章RSS"、"评论RSS"、"WordPress.org"

    一. 去除底部“自豪地采用 WordPress”版权信息: 1.打开主题文件夹:wp-content/themes/twentyeleven/footer.php; 2.找到 这段代码 删除即可: & ...

  5. Borg Maze POJ - 3026 (BFS + 最小生成树)

    题意: 求把S和所有的A连贯起来所用的线的最短长度... 这道题..不看discuss我能wa一辈子... 输入有坑... 然后,,,也没什么了...还有注意 一次bfs是可以求当前点到所有点最短距离 ...

  6. 【题解】 bzoj3956: Count (ST表+单调栈)

    题面 Solution 看了一点点题解,自己又刚了\(2h30min\),刚了出来qwq,我好菜啊qwq 貌似这道题是BZOJ 4826的弱化,弱化都不会qwq凉凉 Solution 首先你可以考虑, ...

  7. JNative 传递参数bug

    下载JNative网址:http://sourceforge.net/projects/jnative/files/jnative/ 下载JNative版本:JNative_1.4RC3_bin.zi ...

  8. numpy 从入门到遗忘

    不常用的函数总是遗忘,很是困扰啊.于是痛下时间,做一个系统的总结,纯原创,都是些实际项目中常用的函数和方法,当然还有一些这边也是没有记录的,因为我在实际数据处理过程中也没有遇到过(如字符串处理等等). ...

  9. AtCoder Grand Contest 010 D - Decrementing

    题目描述 有n个整数,其中第i个数为Ai.这些数字的gcd为1.两人轮流操作,每次操作把一个大于1的数减1,并把所有数除以所有数的最大公约数,最后无法操作者输,求是否先手必胜. 如果当前的sum为偶数 ...

  10. bzoj3209 花神的数论题——数位dp

    题目大意: 花神的题目是这样的 设 sum(i) 表示 i 的二进制表示中 1 的个数.给出一个正整数 N ,花神要问你 派(Sum(i)),也就是 sum(1)—sum(N) 的乘积. 要对1000 ...