Function
题目来源
2017 Multi-University Training Contest - Team 1 - 1006
HDU 6038
题面
描述
给出0到n-1的置换a和0到m-1的置换b
定义从整数0到n-1的集合出发投影到整数0到m-1的集合的映射f。
( 即映射
f:A→B
A={x∈Z∣0≤x≤n−1}
B={x∈Z∣0≤x≤m−1}
)
计算使得对于0到n-1的每一个数,都满足
f(i)=bf(ai)
的映射f有多少个。
两个映射是不同的当且仅当存在至少一个0到n-1的整数在两个映射中投影到两个不同的数。
答案对1e9+7取模。
输入
输入含多组数据。
每组数据中:
第一行有两个数n和m (1≤n≤100000,1≤m≤100000)
第二行有n个0到n-1的数,表示置换a
第三行有m个0到m-1的数,表示置换b
保证 ∑n≤1e6, ∑m≤1e6.
输出
对于每组测试数据,输出一行“Case #x: y
”(没有引号),x表示测试数据的编号(从1开始),y表示当前数据的答案
???
将置换群分解为多个循环节
如图所示为长度为3的循环节a和b
则
f(i)=bf(ai)
表示:
若从a中的节点i出发,依次沿着a边、f边、b边各走一步,可到达b中的节点x,则图中应存在从i到x的f边
如:图中该循环节a的1号节点进行a置换后得到a的2号节点,a的2号节点映射到循环节b中的0号节点,b中的0号节点进行b置换后得到b的1号节点,那么为使该映射f合法,a的1号节点应当可映射到b的1号节点
(即在上图中,若绿边所对应的映射及置换存在,就必存在紫边所对应的映射)
由此
假设循环节a长度为lena 循环节b长度为lenb
用a[1]~a[lena]表示循环节a,将a[1]映射得到的节点标记为b[1],用b[1]~b[lenb]表示循环节b
那么因为a[lena]->a[1]->b[1]->b[2]
所以a[lena]的映射f为b[2]
同理可得:
a[lena-1]->b[3]
a[lena-2]->b[4]
a[lena-3]->b[5]
……
a[1]->b[lena+1]
因为b为循环节,所以b的第lena+1项即b的第(lena+1)%lenb项
所以
因此
对于每一个置换a中的每一个循环节A
设其长度为lena
b中长度lenb为lena约数的循环节B
都可与A构成合法映射
循环节A与B构成的合法映射种类,即lenb
(因为对于循环节A的第一个数,B中的每一个数都可以与之构成映射,而每种对应即为一种最终方案)
最后的最后输出的答案
即对于a的每一个循环节,求出其与b中各循环节构成合法映射的种类数s
a中所有循环节的s相乘即最终的答案
具体实现:
1. 分别计算a和b中每种长度的置换群各有多少个
2. a中长度为i的循环节所能构成的方案数为:对于所有 i%j==0 ,b中长度为j的循环节数目*j(numb[j]*j)
3. a中长度为i的循环节有numa[i]个,因此a中所有长度为i的循环节的总方案数为:长度为i的循环节构成方案数^numa[i]
4. 最终答案为a的各长度循环节构成总方案数的乘积
=另=
人生第一道置换群居然是在比赛打的
想好算法告诉别人别人说再想想通结果我说我先打打看QAQ
结果到我打完都还没有想好_(:з」∠)_
最后二十分钟查错完全放错重点
绝望脸.jpg
明明想通了结果硬是把a循环节相互之间关系全部写成加还查不出的也是没谁了
晚上补题还找不出错又拉了一个别的人听我讲了一遍
“对啊,这里都是对的,那么我们把这些加在一起,就可以得到——欸等等!我为什么要加在一起!”
我可能是个傻子
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#define QAQ 1000000007
const int MAXN=100010;
int n,m;
int numa[MAXN];
long long numb[MAXN];
bool vis[MAXN];
int a[MAXN],b[MAXN];
long long ans;
long long pow(long long x,int n)
{
long long ans=1;
while (n)
{
if (n&1)
ans=(ans*x)%QAQ;
x=(x*x)%QAQ;
n>>=1;
}
return ans;
}
int main()
{
int kase=0;
while (scanf("%d%d",&n,&m)==2)
{
kase++;
memset(numa,0,sizeof(numa));
memset(numb,0,sizeof(numb));
for (int i=0;i<n;i++)
scanf("%d",&a[i]);
for (int i=0;i<m;i++)
scanf("%d",&b[i]);
memset(vis,false,sizeof(vis));
int na=0;
int maxlen=0;
for (int i=0;i<n;i++)
{
if (vis[i])
continue;
int len=1,x=i;
vis[x]=true;
while (!vis[a[x]])
{
x=a[x];
vis[x]=true;
len++;
}
numa[len]++;
if (len>maxlen)
maxlen=len;
}
memset(vis,false,sizeof(vis));
for (int i=0;i<m;i++)
{
if (vis[i])
continue;
int len=1,x=i;
vis[x]=true;
while (!vis[b[x]])cpp
{
x=b[x];
vis[x]=true;
len++;
}
numb[len]++;
}
ans=1;
for (int i=1;i<=maxlen;i++)
if (numa[i])
{
long long s=0;
for (int j=1;j<=i;j++)
if (numb[j] && i%j==0)
s=(s+numb[j]*j%QAQ)%QAQ;
ans=(ans*pow(s,numa[i]))%QAQ;
}
printf("Case #%d: %lld\n",kase,ans);
}
}