hdu--6038--Function

本文探讨了一个关于函数f的问题,给出了具体的输入输出格式及样例,并提供了详细的解题思路和算法实现。通过分析两个置换序列a和b,计算满足特定条件的不同函数f的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Function

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1502    Accepted Submission(s): 707


Problem Description
You are given a permutation  a  from  0  to  n1  and a permutation  b  from  0  to  m1 .

Define that the domain of function  f  is the set of integers from  0  to  n1 , and the range of it is the set of integers from  0  to  m1 .

Please calculate the quantity of different functions  f  satisfying that  f(i)=bf(ai)  for each  i  from  0  to  n1 .

Two functions are different if and only if there exists at least one integer from  0  to  n1  mapped into different integers in these two functions.

The answer may be too large, so please output it in modulo  109+7 .
 

Input
The input contains multiple test cases.

For each case:

The first line contains two numbers  n,   m (1n100000,1m100000)

The second line contains  n  numbers, ranged from  0  to  n1 , the  i -th number of which represents  ai1 .

The third line contains  m  numbers, ranged from  0  to  m1 , the  i -th number of which represents  bi1 .

It is guaranteed that  n106,   m106 .
 

Output
For each test case, output " Case # x y " in one line (without quotes), where  x  indicates the case number starting from  1  and  y  denotes the answer of corresponding case.
 

Sample Input
  
3 2 1 0 2 0 1 3 4 2 0 1 0 2 3 1
 

Sample Output
  
Case #1: 4 Case #2: 4
 

Source

官方题解:

考虑置换  aa  的一个循环节,长度为  ll  ,那么有 $f(i) = b_{f(a_i)} = b_{b_{f(a_{a_i})}} = \underbrace{b_{\cdots b_{f(i)}}}_{l\text{ times }b}$ 。

那么 f(i)f(i) 的值在置换 bb 中所在的循环节的长度必须为 ll 的因数。

而如果 f(i)f(i) 的值确定下来了,这个循环节的另外 l - 1l1 个数的函数值也都确定下来了。

答案就是 \sum_{i = 1}^{k} \sum_{j | l_i} {j \cdot cal_j}i=1kjlijcalj 改为 \prod_{i = 1}^{k} \sum_{j | l_i} {j \cdot cal_j}i=1kjlijcalj ,其中 kk 是置换 aa 中循环节的个数, l_ili 表示置换 aa 中第 ii 个循环节的长度, cal_jcalj 表示置换 bb 中长度为 jj 的循环节的个数。

时间复杂度是 \mathcal{O}(n + m)O(n+m) 。

我的思路:

这题主要的是去找循环节,

分析样例:3 4

2 0 1

0 2 3 1

可以推出:a序列有个长度为3的循环节,即a[0]=2,a[1]=0,a[2]=1。

       b序列有长度为1和3的循环节  长度为1的:b[0]=0;长度为3的:b[1]=2,b[2]=3,b[3]=1;

求解:

1,要满足f(i)=b[f(a[i])];枚举数列a中的循环x,然后同理再找出b中循环y,但需要满足y的长度是x的因子

2,由于循环可以旋转,则每一个长度为len(y)的循环y贡献的方法个数为tol(y)=num(y)*len(y)  (分别表

     示y循环的长度和个数),将该x的每一个tol(y)进行累加,tol(x)=tol(y1)+tol(y2)+....+tol(yn);yi都是

     x的因子。

3,由于不同x之间相互独立,ans=tol(x1)*tol(x2)*.....*tol(xn);

代码:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<bits/stdc++.h>
using  namespace  std;
typedef  long  long ll;
const  int N=1e5+ 10;
const ll mod=1e9+ 7;
int a[N],b[N];
int aa[N],bb[N];
int vis[N];
int main()
{
     int k= 1;
     int n,m;
     while(scanf( "%d%d",&n,&m)!=EOF)
    {
         for( int i= 0; i<n; i++)
            scanf( "%d",&a[i]);
         for( int i= 0; i<m; i++)
            scanf( "%d",&b[i]);
        memset(vis, 0, sizeof(vis));
        memset(aa, 0, sizeof(aa));
        memset(bb, 0, sizeof(bb));
         for( int i= 0; i<n; i++)
        {
             int cnt= 0;
             int x=i;
             while(!vis[x])
            {
                cnt++;
                vis[x]= 1;
                x=a[x];
            }
            aa[cnt]++; ///a该循环节的个数++
        }
        memset(vis, 0, sizeof(vis));
         for( int i= 0; i<m; i++)
        {
             int cnt= 0;
             int x=i;
             while(!vis[x])
            {
                cnt++;
                vis[x]= 1;
                x=b[x];
            }
            bb[cnt]++; ///b该循环节的个数++
        }
        ll sum= 1;
         for( int i= 1; i<=n; i++)
        {
             if(aa[i])
            {
                ll ans= 0;
                 for( int j= 1; j<=i; j++)  ///在b的循环节中找到和a匹配的,即a的因子
                     if(i%j== 0)
                        ans=(ans+bb[j]*j)%mod; ///循环节的个数*循环节的长度,代表循环节里的这些数都可以形成f
                 while(aa[i]--)  ///对于a每个循环节都有ans个,所以*
                    sum=sum*(ans)%mod;
            }
        }
        printf( "Case #%d: ",k++);
        printf( "%I64d\n",sum);
    }
     return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值