1. 程式人生 > >HDU 多校聯賽-Function(圖論)

HDU 多校聯賽-Function(圖論)

題目連結: http://acm.hdu.edu.cn/showproblem.php?pid=6038
Problem Description
You are given a permutation a from 0 to n−1 and a permutation b from 0 to m−1.

Define that the domain of function f is the set of integers from 0 to n−1, and the range of it is the set of integers from 0 to m−1.

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

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

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

Input
The input contains multiple test cases.

For each case:

The first line contains two numbers n, m. (1≤n≤100000,1≤m≤100000)

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

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

It is guaranteed that ∑n≤106, ∑m≤106.

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

有兩個,陣列a是[0~n-1]的排列,陣列b是[0~m-1]的排列。現在定義f(i)=b[f(a[i])];
問f(i)有多少種取值,使得表示式f(i)=b[f(a[i])]全部合法。

尋找都推關係,根據已知的a陣列和b陣列,找出a,b兩個陣列於f的對應關係。

因為提上給出的f(i)=b[f(a[i])],此時我們可以轉換一下,將a[i]看作要求的引數,則上面的式子可以轉 換為f(i)=b[f(a[i])]=b[b[f(a[a[i]])]],如果我們已知這樣轉換下去,直到將最後的結果能夠轉換成f(i)為止 的話。可發現,最裡面的一成是i->a[i]->a[a[i]]···-.i的一個環,這個環的迴圈可以決定f()函式的迴圈, 進而決定外面的b的迴圈,相當於f的迴圈可以由a和b的環來決定。
以第一個樣例 a={1,0,2} b={0,1}為例:
那麼f(0)=b[f(1)] f(1)=b[f(0)] f(2)=b[f(2)]
這裡有兩個環分別為 f(0)->f(1) 和f(2)
所以我們的任務就是在b中找環,該環的長度必須為a中環的長度的約數。
為什麼必須的是約數呢?
因為如果b的環的長度是a的環的長度的約數的話,那也就意味著用b這個環也能構成a這個環,只不 過是多迴圈了幾次而已。
然後找到a中所有環的方案數,累乘便是答案。
為什麼要累乘呢?我最開始一直以為要累加。
這個就用到了排列組合的思想,因為肯定要f(i)肯定要滿足所有的數,而a中的每個環都相當於從a中 取出幾個數的方案數,所以總共的方案數應該累乘。

#include<iostream>
#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
const int max_n=100010;
const int mod=1e9+7;
int n,m;
int a[max_n],b[max_n],len_b[max_n];
bool vis[max_n];
vector<int>A,fac[max_n];
///構建環,並返回環的大小
int dfs(int N,int *c)
{
    if(vis[N])
        return 0;
    vis[N]=1;
    return dfs(c[N],c)+1;
}
void get_fac()
{
    for(int i=1; i<=100000; i++)///fac[j]裡面儲存的是長度為j的環的因子
    {
        for(int j=i; j<=100000; j+=i)
            fac[j].push_back(i);
    }
}
int main()
{
    int Case=0;
    get_fac();
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(int i=0; i<m; i++)
            scanf("%d",&b[i]);
        A.clear();
        memset(vis,0,sizeof(vis));
        for(int i=0; i<n; i++)
        {
            if(vis[i]) continue;
            A.push_back(dfs(i,a));///a陣列中環的長度,重複的長度也是儲存的
        }
        memset(vis,0,sizeof(vis));
        memset(len_b,0,sizeof(len_b));
        for(int i=0; i<m; i++)
        {
            if(vis[i]) continue;
            len_b[dfs(i,b)]++;///b陣列中長度為dfs(i,b)的環的個數
        }
        long long int ans=1;
        for(int i=0,L=A.size(); i<L; i++)
        {
            int la=A[i];///取出a中的一個長度為la的環
            long long res=0;
            for(int j=0,ll=fac[la].size(); j<ll; j++)
            {
                int lb=fac[la][j];///lb是長度為la的環的一個因子
                res=(res+(long long)lb*len_b[lb])%mod;///因子個數乘以環的個數就是一功德方案數
            }
            ans=ans*res%mod;
        }
        printf("Case #%d: %lld\n",++Case,ans);
    }
    return 0;
}