Hdu 6038 Function【思维+强连通找环】

Function

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


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


题目大意:


给你一个数组A,和一个数组B,数组A是【0~n-1】的排咧,数组B是【0~m-1】的排列。

现在定义F(i)=bF(ai);

问有多少种取值,使得F(i)全部合法。

样例1可行的解:

110

111

001

000


思路:


写出样例2的公式:


①F(0)=bF(2)

②F(1)=bF(0)

③F(2)=bF(1)


我们不难发现,如果我们设定了F(0)的值,就能够通过式子②能够得知F(1)的值,然后就能通过式子③得知F(2)的值,最后再回归式子①尝试当前设定的值是否合法了。


那么这里有一个环。

所以我们的解题关键就是找环。


那么我们O(n)就能够找到数组A中所有的环的信息,能够得知环的个数和每个环的长度。

那么我们再O(m)能够找到数组B中所有环的信息,能够得知环的个数和每个环的长度。


我们对于A数组中的一个环的话如果一个环中的任意一个点的价值我们能够设定出来,那么这一个环的所有点的值就都能够知道了。

然而这个能够设定的值,肯定是数组B中的一个值,而且我们已知都是环,那么数组B中的这个被选中设定的值也一定存在一个环,而且这个环的长度,一定是A长度环的因子长度。


那么我们O(n)找A数组的环,O(m)找B数组的环,然后对于A数组中长度为D的一个环,如果B数组中有一个环的长度为d,并且如果D%d==0.那么这个B数组的这个环的所有值,都可以作为A数组中这个环的值。那么对于A数组中的这个环来讲,答案数就多了d个。


过程统计每个环能够满足的答案的个数,然后累乘即可。

找环的信息不一定要用强连通来找,直接O(n)去找也行。



Ac代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<map>
using namespace std;
#define ll __int64
vector<int>mp[100505];
int a[150000];
int b[150000];
int stack[150000];
int color[155000];
int num[155000];
int num2[155000];
int dfn[155000];
int low[155000];
int vis[155000];
ll ans[155000];
map<int, ll >s;
int n,m;
int sig,cnt,tt,presig;
const ll MOD=1e9+7;
void Tarjan(int u)
{
    vis[u]=1;
    dfn[u]=low[u]=cnt++;
    stack[++tt]=u;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis[v]==0)Tarjan(v);
        if(vis[v]==1)low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        sig++;
        do
        {
            color[stack[tt]]=sig;
            vis[stack[tt]]=-1;
        }
        while(stack[tt--]!=u);
    }
}
void Slove()
{
    cnt=1,sig=0,tt=-1;
    memset(num,0,sizeof(num));
    memset(stack,0,sizeof(stack));
    memset(color,0,sizeof(color));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==0)Tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        num[color[i]]++;
    }
    presig=sig;
}
void Slove2()
{
    cnt=1,sig=0,tt=-1;
    memset(num2,0,sizeof(num2));
    memset(stack,0,sizeof(stack));
    memset(color,0,sizeof(color));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++)
    {
        if(vis[i]==0)Tarjan(i);
    }
    for(int i=1;i<=m;i++)
    {
        num2[color[i]]++;
    }
    for(int i=1;i<=m;i++)
    {
        s[num2[i]]+=num2[i];
    }
}
int main()
{
    int kase=0;
    while(~scanf("%d%d",&n,&m))
    {
        s.clear();
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]++;
        for(int i=1;i<=m;i++)scanf("%d",&b[i]),b[i]++;
        for(int i=1;i<=n;i++)
        {
            mp[a[i]].push_back(i);
        }
        printf("Case #%d: ",++kase);
        Slove();
        for(int i=1;i<=m;i++)mp[i].clear();
        for(int i=1;i<=m;i++)
        {
            mp[b[i]].push_back(i);
        }
        Slove2();
        for(int i=1;i<=presig;i++)
        {
            for(int j=1;j<=sqrt(num[i]);j++)
            {
                if(num[i]%j==0)
                {
                    ans[i]+=s[j];
                    if(num[i]/j!=j)
                    ans[i]+=s[num[i]/j];
                }
            }
        }
        ll output;
        for(int i=1;i<=presig;i++)
        {
            if(i==1)output=ans[i];
            else output*=ans[i];
            output%=MOD;
        }
        printf("%I64d\n",output);
    }
}










  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值