思路:
看似是数学题。。其实是图论。
我们可以发现他给的函数的约束关系其实是一种递推推导。
对于样例
3 2
1 0 2
0 1
我们把a行的第i个(i从0开始)建一条从i到
ai
的边,那么对于这个样例我们得到了一个二元环和一个一元环,对b行进行相应的操作,我们得到了两个一元环。(我这里的二元环即代表由两个元素组成的环)
这个环是什么意思呢?她代表了一种相互递推的关系,只要在环中的任意一个元素被决定了,环中其余的元素也就都确定了。正因为有这样的性质,所以我们才建图来找出各种环的数量。
tarjan跑两遍分别求出a行中各种环的数量和b行中各种环的数量。
我在这里把这些信息分别存在
aa[n]
和
bb[m]
中。其中
aa[i]
表示i元环的数量为
aa[i]
个。
然后对于每个非0的
aa[i]
,我们需要算出有多少
bb[i]
能够放进去。比如
aa[3]=1
而
bb[1]=1
bb[3]=1
那么我们应该得到的答案即: 1*bb[1] + 3*bb[3] (此处算的是每个aa[i]的取的种数,最后答案需要将这些相乘)
注意环的关系,只有当a中的环的元的个数是b中环的元的个数的整数倍时,我们才能将b中的环放到a中而不引起关系的崩溃。这里采用类似素数筛的方式,直接一遍扫出来。
//b中最多有m元环 sumb数组中保存了aa[i]对应的有多少种取法
for(int i = 1;i <= m;i++){
if(bb[i] == 0) continue;
for(int j = i;j <= n;j+=i){
sumb[j] += i*bb[i];
}
}
再之后,我们需要注意这是在考虑aa[i]为1的情况下的结果,如果aa[i]不为1而是为n,那么我们需要为这n种i元环分别选择一种,即
sumb[i]n
所以如下:
lli ans = 1;
for(lli i = 1;i <= n;i++){
if(aa[i] != 0){//只有当此种环存在时才算
ans *= qp(sumb[i],aa[i]);//quick power 快速幂
ans %= mod;
}
}
ac代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <string.h>
#include <algorithm>
#define inf 0x3f3f3f3f
typedef long long int lli;
using namespace std;
const int maxn = 120000;
const int mod = 1e9+7;
struct edge{
lli to,v,next;
}ed[maxn];lli cnte,head[maxn];
void ae(lli x,lli y){
ed[++cnte].to = y;
ed[cnte].next = head[x];
head[x] = cnte;
}
lli aa[maxn],bb[maxn],sumb[maxn],dp[maxn];
lli dfn[maxn],low[maxn],vis[maxn],stak[maxn],cntc,cnts,index;
void dfs(int u,lli *ss){
dfn[u]=low[u] = ++index;
stak[cnts++]=u;
vis[u]=1;
for(int i = head[u];~i;i=ed[i].next){
lli v = ed[i].to;
if(!dfn[v]){
dfs(v,ss);
low[u] = min(low[u],low[v]);
}
else if(vis[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
cntc++;lli v,cnt = 0;
do{
cnt++;
v = stak[--cnts];
vis[v] = 0;
}while(v!=u);
ss[cnt]++;
}
}
lli n,m;
void tarjan(lli *ss,lli n){
for(int i = 0;i < n;i++){
if(!dfn[i]) dfs(i,ss);
}
}
inline lli qp(lli a,lli x){
if(a == 0) return 0;
lli ans = 1;
for(;x;x>>=1){
if(x&1) ans = ans*a % mod;
a = a*a % mod;
}
return ans % mod;
}
void ini(){
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
cnte = cntc = cnts = index = 0;
}
int main(){
int cas = 0;
while(~scanf("%I64d%I64d",&n,&m)){
ini();
memset(aa,0,sizeof(aa));
memset(bb,0,sizeof(bb));
memset(sumb,0,sizeof(sumb));
cas++;
int temp;
for(int i = 0;i < n;i++){
scanf("%d",&temp);
ae(i,temp);
}
tarjan(aa,n);
ini();
for(int i = 0;i < m;i++){
scanf("%d",&temp);
ae(i,temp);
}
tarjan(bb,m);
for(int i = 1;i <= m;i++){ //类似素数筛
if(bb[i] == 0) continue;
for(int j = i;j <= n;j+=i){
sumb[j] += i*bb[i];
}
}
lli ans = 1;
for(lli i = 1;i <= n;i++){
if(aa[i] != 0){
ans *= qp(sumb[i],aa[i]);
ans %= mod;
}
}
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}