luogu P4778 Counting swaps
计数套路题?但是我连套路都不会,,,
拿到这道题我一脸蒙彼,,,感谢@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的更多相关文章
- 洛谷P4778 Counting swaps 数论
正解:数论 解题报告: 传送门! 首先考虑最终的状态是固定的,所以可以知道初始状态的每个数要去哪个地方,就可以考虑给每个数$a$连一条边,指向一个数$b$,表示$a$最后要移至$b$所在的位置 显然每 ...
- P4778 Counting Swaps 题解
第一道 A 掉的严格意义上的组合计数题,特来纪念一发. 第一次真正接触到这种类型的题,给人感觉好像思维得很发散才行-- 对于一个排列 \(p_1,p_2,\dots,p_n\),对于每个 \(i\) ...
- CH3602 Counting Swaps
题意 3602 Counting Swaps 0x30「数学知识」例题 背景 https://ipsc.ksp.sk/2016/real/problems/c.html Just like yeste ...
- Counting swaps
Counting swaps 给你一个1-n的排列,问用最少的交换次数使之变为递增排列的方案数\(mod\ 10^9+7\),1 ≤ n ≤ 10^5. 解 显然最少的交换次数不定,还得需要找到最小交 ...
- LFYZOJ 104 Counting Swaps
题解 #include <iostream> #include <cstdio> #include <algorithm> #include <cmath&g ...
- lfyzoj104 Counting Swaps
问题描述 给定你一个 \(1 \sim n\) 的排列 \(\{p_i\}\),可进行若干次操作,每次选择两个整数 \(x,y\),交换 \(p_x,p_y\). 请你告诉穰子,用最少的操作次数将给定 ...
- luoguP4778 Counting swaps
题目链接 题解 首先,对于每个\(i\)向\(a[i]\)连边. 这样会连出许多独立的环. 可以证明,交换操作不会跨越环. 每个环内的点到最终状态最少交换步数是 \(环的大小-1\) 那么设\(f[i ...
- 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 ...
- 线段树合并 || 树状数组 || 离散化 || BZOJ 4756: [Usaco2017 Jan]Promotion Counting || Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数
题面:P3605 [USACO17JAN]Promotion Counting晋升者计数 题解:这是一道万能题,树状数组 || 主席树 || 线段树合并 || 莫队套分块 || 线段树 都可以写..记 ...
随机推荐
- 如何取消浏览器护眼色 Lodop打印图片有窗口颜色的边框
Lodop打印图片出现了边框,然而通常情况下是没有边框的,由于Lodop是基于本机的ie进行解析的,和IE的设置有关.用户的电脑和习惯千差万别,有人喜欢给浏览器加上护眼色,而这一个行为可能导致Lodo ...
- liunx速查
文件和目录 Linux 主要目录速查表 /:根目录,一般根目录下只存放目录,在 linux 下有且只有一个根目录,所有的东西都是从这里开始 当在终端里输入 /home,其实是在告诉电脑,先从 /(根目 ...
- Sabotage UVA - 10480 (输出割边)
题意:....emm...就是一个最小割最大流,.,...用dinic跑一遍.. 然后让你输出割边,就是 u为能从起点到达的点, v为不能从起点到达的点 最后在残余路径中用dfs跑一遍 能到达的路 ...
- HDU - 4725 (The Shortest Path in Nya Graph)层次网络
题意:有n个点,每个点都在一个层内,层与层之间的距离为c,一个层内的点可以到达与它相邻的前后两个层的点,还有m条小路 ..时间真的是卡的很恶心啊... 借一下别人的思路思路: 这题主要难在建图上,要将 ...
- day29 上周复习
上周内容回顾 初始面向对象 class 类名(父类1,父类2): 静态属性 = "" # 静态属性,类方法 def __init__(self): # 初始化方法 def func ...
- 【POJ1456】Supermarket(贪心)
BUPT2017 wintertraining(16) #4 F POJ - 1456 题意 每个商品有过期日期和价格,每天可以卖一个商品,必须在过期前出售才能收益,求最大收益. 题解 贪心,按价格排 ...
- [luogu1131][bzoj1060][ZJOI2007]时态同步【树形DP】
传送门:https://www.luogu.org/problemnew/show/P1131 题目大意 给你一棵树,每条边有边权,要求增加一些边的边权,使得根节点到每个叶子节点的距离相等,求出最少共 ...
- [hgoi#2019/3/3]赛后总结
T1--最长公共前缀(lcp) 定义两个字符串S,T 的最长公共前缀lcp(S,T)为最长的字符串R,满足R 既是S 的前缀又是T 的前缀. 给定一个字符串S,下标从1 开始,每次询问给出四个正整数a ...
- 洛谷 P1057 传球游戏 解题报告
P1057 传球游戏 题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹 ...
- (转)Maven仓库——私服介绍
背景:对Maven私服一直想做个深入的总结,因为不了解,所以感觉很陌生. 转载地址:http://blog.csdn.net/catoop/article/details/62312477 常用功能和 ...