洛谷P4778 Counting swaps 数论
正解:数论
解题报告:
首先考虑最终的状态是固定的,所以可以知道初始状态的每个数要去哪个地方,就可以考虑给每个数$a$连一条边,指向一个数$b$,表示$a$最后要移至$b$所在的位置
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。显然每个数只会有一条出边,也只会有一条入边,所以会构成若干条环然后现在的目标就相当于是要通过最少的次数使所有边都变成自环
然后考虑这个交换操作,就相当于是交换两条边的终点
欧克把题目转化完了下面考虑解题
先证明这样一个结论:一个长度为$n$的环要变成$n$个自环至少需要$n-1$步
证明如下:
考虑用数学归纳法
首先长度为2的环要变成2个自环显然是1步
现在考虑若已证明对长度$<k$的环都成立
则对于长度为k的环,显然随意交换两条边的终点后就变成了一个长度为$x$的环和一个长度为$y$的环,且$x+y=k$
那么分别对这两个环都有最少步骤为$(x-1)+(y-1)=k-2$
再加上第一步交换两条边的终点的操作,总的步骤就要$k-2+1=k-1$步
得证(,,,这个证明挺弱智的我$jio$得
接着考虑设$f[x]:$拆长度为$x$的环为$x$个自环的方案数
为了表达方便再设个$g[x][y]$表示长度为$x+y$的环拆成长为$x$的环和长为$y$的环的方案数
首先显然有$g[x][y]=\left\{\begin{matrix}\frac{n}{2}(x=y)\\n(x\neq y)\end{matrix}\right.$
瞎证明下趴,下面为了表达方便提前解释下,交换点对的意思就是交换终点为这两个点的边的终点(,,,有点绕,怪我语文太差$TT$
显然当$x\neq y$的时候
对任意一个点$x$,会有两个点$y_{1}$,$y_{2}$满足交换$\left ( x,y \right )$之后能拆成一个大小为x的环和一个大小为y的环
然后再考虑重复,就每条边会被枚举两次(显然?就两个断点分别作为起点枚举一次嘛$QwQ$,所以就有$g[x][y]=\frac{n\cdot 2}{2}=n$
然后当$x=y$的时候,就显然只有一个点$y$了,所以就是$g[x][y]=\frac{n}{2}$
证毕
然后考虑$f[x]$的递推式?
不难想到$f[x]=\sum_{i=1}^{\frac{x}{2}}g[i][x-i]\cdot f[i]\cdot f[x-i]\cdot \frac{(x-2)!}{(i-1)!\cdot (x-i-1)!}$
再瞎证下$QAQ$
其实前面都不太难get,就枚举拆成的环有多大,然后拆成大小为$i,x-i$的方案数是$g[i][x-i]$,然后内部拆的方案是$f[i]\cdot f[x-i]$
主要大概是要解释下后面这个$\frac{(n-2)!}{(i-1)!\cdot (n-i-1)!}$
就因为两个环是互相独立互不影响的,所以显然可以先做一步第一个环的,再做一步第二个环的,这样子,所以总共有$(n-2)$步就有$(n-2)!$条方案
但考虑到事实上在分别做两个小环的时候就已经考虑到这个问题已经乘过了,所以就再除以一个$(i-1)!\cdot (n-i-1)!$
但是实际上这儿有个优化,,,因为这个复杂度实际上是过不去的,复杂度$O(n^{2})$的,但是可以打表找规律得$f[i]=i^{i-2}$
不会证啊$QAQ$那就不证了趴$QAQ$
最后考虑因为是有若干个环,同样是因为环之间互不影响可以先做一步这个环的再做一步那个环的这样子,所以最后的$ans$就是$(\prod_{i=1}^{k}f_{l_i}) \cdot \dfrac{(n-k)!}{\prod_{i=1}^k(l_i-1)!}$
$aya$好像忘记说$l_i$辣,,,,就是说每个环的大小辣
那就做完辣?等下放代码吼QwQ!

#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define t(i) edge[i].to #define int long long #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) #define e(i,x) for(ri i=head[x];i;i=edge[i].nxt) const int N=1e5+10,mod=1e9+9; int head[N],ed_cnt,ln[N],ln_cnt,as,n,jc[N],inv[N]; bool vis[N]; struct ed{int to,nxt;}edge[N<<1]; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int power(ri x,ri y){if(y<0)return 1;ri ret=1;while(y){if(y&1)ret=1ll*ret*x%mod;x=1ll*x*x%mod;y>>=1;}return ret;} il void pre() { jc[0]=1;rp(i,1,N-10)jc[i]=1ll*jc[i-1]*i%mod; inv[N-10]=power(jc[N-10],mod-2);my(i,N-11,0)inv[i]=1ll*inv[i+1]*(i+1)%mod; } il void ad(ri x,ri y){edge[++ed_cnt]=(ed){x,head[y]};head[y]=ed_cnt;} int dfs(ri x,ri fa){if(vis[x])return 0;vis[x]=1;e(i,x)return dfs(t(i),x)+1;} signed main() { // freopen("4778.in","r",stdin);freopen("4778.out","w",stdout); int T=read();pre(); while(T--) { memset(vis,0,sizeof(vis));memset(head,0,sizeof(head));ed_cnt=0;ln_cnt=0; n=read();rp(i,1,n)ad(i,read());rp(i,1,n)if(!vis[i])ln[++ln_cnt]=dfs(i,0); as=jc[n-ln_cnt];rp(i,1,ln_cnt)as=1ll*as*power(ln[i],ln[i]-2)%mod*inv[ln[i]-1]%mod; printf("%lld\n",as); } return 0; }这儿是代码,,,
