首页 > 其他分享 >LG4778 Counting swaps 题解

LG4778 Counting swaps 题解

时间:2022-11-17 16:56:29浏览次数:84  
标签:include swaps 题解 ll times ifc 操作 LG4778

LG4778 Counting swaps

给定你一个 \(1\sim n\) 的排列 \(p\),可进行若干次操作,每次选择两个整数 \(x,y\),交换 \(p_x,p_y\)。

用最少的操作次数将给定排列变成单调上升的序列 \(1,2,\dots,n\),有多少种方式呢?请你输出方式数对 \(10^9+9\) 取模的结果。

对于一个排列 \(p\),如果 \(i\) 向 \(p_i\) 连边形成一张有向图,显然会形成若干个简单环,目标状态即 \(n\) 个自环。

将一个大小为 \(k\) 的环变成 \(k\) 个自环,至少需要 \(k-1\) 次交换(每次交换至多增加一个环)。

定义 \(f(i)\) 为将一个大小为 \(i\) 的环,在保证交换次数最小的情况下,有多少种方式将其变成目标状态。

考虑每次将一个环分割成两个环,分割出来的两个环大小为 \(x,y\),分割方案有多少种。

显然当 \(x=y\) 的时候,环上有 \(i/2\) 对点,所以方案数为 \(i/2\)。

当 \(x\neq y\) 时候,环上可以选取 \(i\) 个点,每个点与顺时针方向的第 \(x\) 个点向匹配,方案数为 \(i\) 种。

同时分割成两个环之后,两个环内的操作是不会互相影响的,所以相当于有两种操作,第一种操作 \(x\) 个,第二种操作 \(y-1\) 个,最后的操作序列有多少种。即 \(\frac{(x+y-2)!}{(x-1)!(y-1)!}\) 种。综上就是

\[f(x)=\sum_{i=1}^{\lfloor x/2\rfloor} f_i\times f_{x-i}\times \frac{(x-2)!}{(i-1)!(x-i-1)!} \times \begin{cases}x \quad 2\times i\neq n\\ i \quad 2\times i=n\end{cases} \]

然后求出来之后会发现 \(f(x)=x^{x-2}\)。

将所有环的答案合并,由于每个环之间操作时不会互相影响的,所以方案数乘上可重集的排列数(和上方同理)。

\[ans=(\prod_{i=1}^{k}f(siz_i))\times \frac{(n-k)!}{\prod_{i=1}^{k}(siz_i-1)!} \]

总时间复杂度 \(O(n\log n)\),复杂度瓶颈在快速幂求 \(f(x)\)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;

inline ll read(){
	ll x=0,f=1;char ch=getchar();
	while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}

const ll MOD=1000000009;
inline ll fpr(ll b,ll t=MOD-2,ll x=1){
	for(;t>0;t>>=1,b=b*b%MOD)
		if(t&1)x=x*b%MOD;
	return x;
}
namespace binom{
	const int RRN=1000006;
	ll fac[RRN],ifc[RRN];
	inline void binom_init(int n){
		fac[0]=ifc[0]=1ll;
		for(ll i=1;i<=n;++i)fac[i]=fac[i-1]*i%MOD;
		ifc[n]=fpr(fac[n]);
		for(ll i=n-1;i>=1;--i)ifc[i]=ifc[i+1]*(i+1)%MOD;
	}
	inline ll C(ll n,ll r){
		if(n<r)return puts("114514"),-1;
		return fac[n]*ifc[r]%MOD*ifc[n-r]%MOD;
	}
}
using namespace binom;

const int N=1000006;
int n,p[N],tot;
ll f[N],top;bool vis[N];
inline void dfs(int loc){
	vis[loc]=1;++tot;
	if(!vis[p[loc]])dfs(p[loc]);
}

int main(){
	binom_init(1000000);
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		tot=top=0;
		for(int i=1;i<=n;++i)vis[i]=0;
		for(int i=1;i<=n;++i)scanf("%d",&p[i]);
		for(int i=1;i<=n;++i){
			if(!vis[i]){
				int las=tot;
				dfs(i);
				f[++top]=tot-las;
				tot=las;
			}
		}
		ll ans=fac[n-top];
		for(int i=1;i<=top;++i)ans=ans*fpr(f[i],f[i]-2)%MOD;
		for(int i=1;i<=top;++i)ans=ans*ifc[f[i]-1]%MOD;
		printf("%lld\n",ans);
	}
	return 0;
}

标签:include,swaps,题解,ll,times,ifc,操作,LG4778
From: https://www.cnblogs.com/BigSmall-En/p/16899995.html

相关文章

  • 今晚题解
    题意概述给定一张\(n\)个点\(m\)条边的有向图\(G\)。有\(n\)个硬币。初始时有的正面朝上,有的反面朝上。每次你可以手动翻转一枚。如果在\(G\)中有边\(a\righ......
  • Aizu 2378 SolveMe 题解 (置换,计数)
    题目链接题意简述有一个n个点的有向图,每个点有两条出边,称为A边和B边。称一种构图是好的,当且仅当对于所有i,从第i个点出发,先连走x次A边,走1次B边,再连走y次A边,走1次B边,再走z......
  • CF1181C Flag 题解
    题意:问在一个\(n\timesm\)的平面里有多少旗帜,旗帜的定义是三条宽度相等的带子,相邻的带子颜色不能相同(第一和第三条的颜色可以相同)。考虑以左上角统计旗帜,预处理每个点......
  • 小程序获取不到用户头像和昵称返回微信用户问题解决,即小程序授权获取用户头像规则调整
    最近好多同学在学习石头哥小程序课程的时候,遇到了下面这样的问题,在小程序授权获取用户头像和昵称时,获取到的是下面这样的。到底是什么原因导致的呢,去小程序官方文档一看,又是......
  • Codeforces Gym 100958 E Cellular Automaton (Makoto rng_58 Soejima contest) 题解
    题目链接其实"序列中1的数量有限"是一个非常重要的条件。这意味着我们可以找到序列中的第一个1和最后一个1。考虑这样一件事情:初始时我们把一个长度为\(2^{2w+1}\)的"滑......
  • CF707E Garlands 题解
    简要题意:在一个\(n\timesm\)的矩阵(\(n,m\le2000\))中,每个点都有个灯,刚开始所有灯都是亮的,每个灯都有一个颜色(\(k\le2000\))和一个权值,保证所有颜色相同的点是联通块。现......
  • UVA1331 题解
    前言题目传送门!更好的阅读体验?计算几何、区间DP。思路......
  • T292306 01最短路 题解
    又是一个找不到题目所以自己写的题。。。40迪杰斯特拉,但是搞不懂为什么是wa而不是re的#include<bits/stdc++.h>#definefor1(i,a,b)for(inti=a;i<=b;i++)#definell......
  • 紫罗兰题解
    题意概述给定一张\(n\)个顶点\(m\)条边的无向图,顶点的编号在\(1\simn\)内,第\(i\)条无向边连接着顶点\(x_i\)与\(y_i\)。我们称顶点\(v_0,v_2,\cdots,v_{k......
  • DTOJ 2022-11-15 测试 题解
    测试成果100+100+50+10=260还行吧(虽然T2做法很迷惑)A惊鸿(grace)DTOJP6367题面大意给定一个\(n\)行\(m\)列的仅包含小写字母的矩阵\(A\)。求从\((1,1)\)......