P4778 Counting Swaps 题解

第一道 A 掉的严格意义上的组合计数题,特来纪念一发。
第一次真正接触到这种类型的题,给人感觉好像思维得很发散才行……



对于一个排列 \(p_1,p_2,\dots,p_n\),对于每个 \(i\) 向 \(p_i\) 连一条边,可以发现整个构成了一个由若干环组成的图,目标是将这些环变为自环。
引理:把长度为 \(n\) 的环变为 \(n\) 个自环,最少交换次数为 \(n-1\)。
用归纳法证,对于当前情况,任意一次交换都将其拆为两个环,由淘汰赛法则可知引理成立。
记 \(F_n\) 表示在最少交换次数下把长度为 \(n\) 的环变为 \(n\) 个自环,有多少种交换方式。由前所述,我们每次都将其拆为两个环,不妨设两个环长度为 \(x,y\),并记 \(T(x,y)\) 表示有多少种方法可将一个长度为 \(n\) 的环变为长度为 \(x,y\) 的两个环,那么当 \(n\) 为偶数且 \(x=y\) 时有 \(T(x,y)=\frac{n}{2}\),其他情况 \(T(x,y)=n\)。由于 \(x\) 环与 \(y\) 环的操作互不干扰,两边的操作可以随意排列,因此这里就是一个多重集的全排列。
于是有了递归表达式: \[F_n=\sum_{x+y=n}\left(T(x,y)F_xF_y\frac{(n-2)!}{(x-1)!(y-1)!}\right)\] 对于题目中的所有 \(k\) 个环,记它们的长度为 \(\{l_k\}\),最终答案为: \[\prod^{k}_{i=1}F_{l_{i}}\dfrac{(n-k)!}{\prod^{k}_{i=1}(l_i-1)!}\] 还没完,经过JOJO的洗礼之后发现 \(F_n=n^{n-2}\)!复杂度立马降为 \(O(n\log n)\)……
(找环这个骚操作是照题解区 dalao 学的,只能 orz

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+5;
const ll mod=1e9+9;
int n,a[N],L[N],cnt;
ll fac[N]={1,1},F[N]={0,1};
bool vis[N];

ll qpow(ll bas,ll p)
{
    ll res=1; bas%=mod;
    for(;p;p>>=1)
    {
        if(p&1) res=res*bas%mod;
        bas=bas*bas%mod;
    }
    return res;
}

int dfs(int x)
{
    vis[x]=1;
    if(vis[a[x]]) return 1;
    return dfs(a[x])+1;
}

int main()
{
    int T; scanf("%d",&T);
    for(int i=2;i<N;++i)
        fac[i]=i*fac[i-1]%mod,
        F[i]=qpow(i,i-2)%mod;
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        cnt=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)
            if(!vis[i]) L[++cnt]=dfs(i);
        ll ans=fac[n-cnt];
        for(int i=1;i<=cnt;++i)
            ans=ans*F[L[i]]%mod*qpow(fac[L[i]-1],mod-2)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wzzyr24/p/12234527.html

时间: 2024-10-02 07:28:34

P4778 Counting Swaps 题解的相关文章

luogu P4778 Counting swaps

计数套路题?但是我连套路都不会,,, 拿到这道题我一脸蒙彼,,,感谢@poorpool 大佬的博客的指点 先将第\(i\)位上的数字\(p_i\)向\(i\)连无向边,然后构成了一个有若干环组成的无向图,可以知道某个点包含它的有且仅有一个环,因为所有点度数都为2(自环的点度数也是2吧qwq) 那么我们的最终目标就是把这个图转换成有\(n\)个自环的图,相当于把排列排好顺序 考虑对于一个\(m\)个点的环,把它变成\(m\)个自环至少需要\(m-1\)步(相当于排列\((2,3...m,1)\)变

POJ 2386 Lake Counting 搜索题解

简单的深度搜索就可以了,看见有人说什么使用并查集,那简直是大算法小用了. 因为可以深搜而不用回溯,故此效率就是O(N*M)了. 技巧就是增加一个标志P,每次搜索到池塘,即有W字母,那么就认为搜索到一个池塘了,P值为真. 搜索过的池塘不要重复搜索,故此,每次走过的池塘都改成其他字母,如'@',或者'#',随便一个都可以. 然后8个方向搜索. #include <stdio.h> #include <vector> #include <string.h> #include

counting swaps

3602 Counting Swaps 0x30「数学知识」例题 背景 https://ipsc.ksp.sk/2016/real/problems/c.html Just like yesterday (in problem U of the practice session), Bob is busy, so Alice keeps on playing some single-player games and puzzles. In her newest puzzle she has a

lfyzoj104 Counting Swaps

问题描述 给定你一个 \(1 \sim n\) 的排列 \(\{p_i\}\),可进行若干次操作,每次选择两个整数 \(x,y\),交换 \(p_x,p_y\). 请你告诉穰子,用最少的操作次数将给定排列变成单调上升的序列 \(1,2,\ldots,n\),有多少种方式呢?请输出方式数对 \(10^9+9\) 取模的结果. 输入格式 第一行一个整数 \(T\) 代表数据组数. 每一组测试数据,第一行是一个整数 \(n\) 代表排列中的元素个数,第二行 \(n\) 个整数,是这个排列. 输入数据中

【题解】晋升者计数 Promotion Counting [USACO 17 JAN] [P3605]

[题解]晋升者计数 Promotion Counting [USACO 17 JAN] [P3605] 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训.!牛是可怕的管理者! [题目描述] 奶牛从 \(1\) ~ \(N(1≤N≤1e5)\) 进行了编号,把公司组织成一棵树,\(1\)号奶牛作为总裁(树的根节点).除总裁以外的每头奶牛都有且仅有唯一的一个的上司(即它在树上的父结点).每一头牛\(i\)都有一个不同的能力指数 \(p(i)\),描述了她对其工作的擅长程度.如果奶牛

POJ 2386 - Lake Counting 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2386 题目大意: 给出一张N*M的地图(1<=N,M<=100),地图上的W表示水坑,.表示陆地.水坑是八方向连通的,问图中一共有多少个水坑. Sample Input 9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0 Sample Output 6 5 分析: 非常简单的深搜.两重循环遍历这个地图,遇到W就dfs来求出这一整个大水坑,并把搜索过

洛谷 P1596 [USACO10OCT]湖计数Lake Counting 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1596 题目描述 Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100

PAT甲题题解-1004. Counting Leaves (30)-统计每层叶子节点个数+dfs

统计每层的叶子节点个数建树,然后dfs即可 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; /* 统计每层的叶子节点个数 建树,然后dfs即可 */ const int maxn=105; int n,m; int layer[maxn]; //统计每层的叶子节点

PAT甲题题解-1049. Counting Ones-数学问题

n位数,总共有0~10^n-1共计10^n个数那么所有数出现的总次数变为n*(10^n)个数1出现的次数便是十分之一,所以n位数中,1出现的次数为n*10^(n-1)知道这一个后,接下来就方便求了. 举个例子就方便理解了 3125 从头到尾for一遍 3:那么便有三组1000以内的:0~999,1000~1999,2000~29991000以内的1的个数为300,所以共有3*300=900但是又因为1000~1999中千位上的1也要算进去,有1000个所以0~2999中总共有900+1000=1