hdu-6038 Function 思維+模擬
阿新 • • 發佈:2018-12-11
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6038
題意:
有點複雜,耐心觀看。給你一個長度為n下標從0~n-1的a陣列,和一個長度為m下標從0~m-1的b陣列,兩個陣列均為一組排序(即0~n-1種每一個數都只出現一次)。要你找出有多少種函式f的對映,使得對,都有成立。
做法:
重點就在這兩個陣列都是排序!所以對於a和b陣列來說,裡面的數字可以組成至少一個環,什麼意思呢。假設我們在a陣列中有a[1]=3,a[3]=6,a[6]=1.那麼這就有1->3,3->6,6->1的一個環,環的大小(即環內的節點數)就是3。由於說明是排序,所以保證兩陣列中均有至少一個兩兩不相交的環,我們將這些環的大小和記錄下來後,會發現,在我們對0~n-1進行處理時,如果a中有一個環的大小為x,b中有一個環的大小為y並且有x%y=0使,那麼y中的數字可以成為x對映的物件。假設有a中有1->2,2->3,3->4,4->5,5->6,6->1,b中有 0->5,5->0時,令f(1)=0,自然就有f(2)=5,f(3)=0...,這便是一組對映,當f(1)=5時,能得到不同的對映。那麼答案自然就出來了。
我們列舉a裡的每一個環,去找是否存在b裡有環數等於它的因子x,如果有,那麼這個環的方案要加上這個因子x*x出現的次數,最後a裡每一個環的數量相乘即可。有個小細節,如果有一個環它沒有可行,那麼最終為0
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const ll mod=1e9+7; const int maxn=100005; vector<ll> yinzi[maxn]; ll a[maxn],b[maxn],n,m; map<ll,ll> numb,numa; map<ll,ll>::iterator it; bool visa[maxn],visb[maxn]; void init(){ yinzi[1].pb(1); for(int i=2;i<maxn-2;i++){ yinzi[i].pb(1ll); for(int j=i;j<maxn;j+=i){ yinzi[j].pb((ll)i); } } } ll quick(ll a,ll b){ ll ans=1; while(b){ if(b&1) ans=ans*a%mod; a=a*a%mod; b/=2; } return ans; } int main(){ init(); int cas=0; while(~scanf("%lld%lld",&n,&m)){ memset(visa,0,sizeof(visa)); memset(visb,0,sizeof(visb)); numb.clear();numa.clear(); for(int i=0;i<n;i++) scanf("%lld",&a[i]); for(int i=0;i<m;i++) scanf("%lld",&b[i]); for(int i=0;i<n;i++){ ll innum=0; if(!visa[i]){ int now=i; while(!visa[now]){ innum++; visa[now]=1; now=a[now]; } numa[innum]++; } } for(int i=0;i<m;i++){ int innum=0; if(!visb[i]){ int now=i; while(!visb[now]){ innum++; visb[now]=1; now=b[now]; } numb[innum]++; } } ll ans=1; for(it=numa.begin();it!=numa.end();it++){ ll aim=it->first,num=it->second; //cout<<aim<<" "<<num<<endl; ll tmpans=0; for(int i=0;i<yinzi[aim].size();i++){ ll yi=yinzi[aim][i]; tmpans+=(ll)numb[yi]*yi; } ans=(ans*quick(tmpans,num))%mod; } printf("Case #%d: %lld\n",++cas,ans); } return 0; }