2017多校第一套&&hdu6038 思維 數學
鏈接 http://acm.hdu.edu.cn/showproblem.php?pid=6038
題意:
給你一個a序列,代表0到n-1的排列;一個b序列代表0到m-1的排列。問你可以找出多少種函數關系f,f的定義域內的i都滿足f(i)=b[f(a[i])];
分析:這個主要是找循環節 循環節導致函數有多種情況 找到每段循環節的 取值 種數 相乘起來就是答案
比如說:如果 a 序列是 2 0 1 那麽我們可以發現
f[0] = b[f(a[0])] f[0] = b[f(2)]
f[1] = b[f(a[1])] f[1] = b[f(0)]
f[2] = b[f(a[2])] f[2] = b[f(1)]
對於這組數來說,假如我們先指定了f(0)對應的在b中的值,那麽根據第2個式子,就可以得出f(1),根據f(1)就又可以得出f(2),最後根據f(2)就可以檢驗f(0)的值是否正確。
仔細觀察左邊的柿子 (定義域)i與a[ i ] 是循環的 如果想使上述成立,必須右邊的柿子(值域)i與b[ i ] 也存在其約數長度的循環節。
如果不是約數長度的循環節會使上述假設矛盾 自己寫一下就知道了 只有被整出才會成立。
每個定義域的循環節都可以與每個值域的循環節匹配(滿足紅字匹配成功)
那麽就是找兩個序列的不相交的循環節,對於定義域裏面的每一個循環節都找出來有多少種情況。每一個循環節的情況數為它所能匹配的值域循環節的長度 和。
樣例一 (1+1)*(1+1)=4; a兩個環(長度 2 1) b兩個個環(長度 1 1) 2與1 1匹配 1與1 1 匹配
樣例二 (1+3)=4; a一個環(長度 3) b兩個環(長度 1 3) 3與1 3匹配
註 對於一個a循環節 若b循環節中沒有能與之匹配的 答案為零 想想為什麽 不用特判累積過程中可以處理掉
AC代碼
1 #include <stdio.h> 2 #include <math.h> 3#include <string.h> 4 #include <stdlib.h> 5 #include <iostream> 6 #include <sstream> 7 #include <algorithm> 8 #include <string> 9 #include <queue> 10 #include <vector> 11 using namespace std; 12 const int maxn= 1e5+10; 13 const int mod= 1e9+7; 14 const int inf = 0x3f3f3f3f; 15 typedef long long ll; 16 int n,m; 17 int a[maxn],b[maxn]; 18 int vis[maxn],loop_a[maxn],loop_b[maxn]; //loop存循環節長度 19 int main() 20 { 21 int kase=0; 22 while(scanf("%d %d",&n,&m)!=EOF) 23 { 24 for(int i=0; i<n; i++) 25 scanf("%d",&a[i]); 26 for(int i=0; i<m; i++) 27 scanf("%d",&b[i]); 28 memset(vis, 0, sizeof(vis)); //標記是否訪問過 找不相交的循環節 29 memset(loop_b, 0, sizeof(loop_b)); 30 memset(loop_a, 0, sizeof(loop_a)); 31 int cnt1=0,cnt2=0; //cnt記錄循環節個數 32 for(int i = 0; i < n; i++) 33 { 34 if(!vis[i]) //過程自己模擬一下就明了了 35 { 36 int x = a[i]; 37 int len = 0; 38 while(!vis[x]) 39 { 40 len++; 41 vis[x] = 1; 42 x = a[x]; 43 } 44 if(len!=0) 45 loop_a[cnt1++]=len; //保存循環節 46 } 47 } 48 memset(vis, 0, sizeof vis); 49 for(int i = 0; i < m; i++) //b同理 50 { 51 if(!vis[i]) 52 { 53 int x = b[i]; 54 int len = 0; 55 while(!vis[x]) 56 { 57 len++; 58 vis[x] = 1; 59 x = b[x]; 60 } 61 if(len!=0) 62 loop_b[cnt2++]=len; 63 } 64 } 65 ll ans=1; 66 for(int i=0; i<cnt1; i++) //枚舉a與b的循環節 試了不會超時 還有更快一些的做法 67 { 68 ll sum=0; //記錄每個循環節的情況數 69 for(int j=0; j<cnt2; j++) 70 { 71 if(loop_a[i]%loop_b[j]==0) //只有是約數才能匹配 72 { 73 sum=(sum+loop_b[j])%mod; //加進去 取模 74 } 75 } 76 ans=(ans*sum)%mod; //乘到答案裏面 77 } 78 printf("Case #%d: %lld\n",++kase,ans); 79 } 80 return 0; 81 }
作者水平有限 歡迎大佬糾錯!
多校真jb難啊~~~~~QAQ
太菜了
2017多校第一套&&hdu6038 思維 數學