【題目】ios
【描述】ide
有m個n排列,求一共有多少個公共子段。spa
數據範圍:1<=n<=100000,1<=m<=103d
【思路】code
對於第一個排列來講,若是第k個位置開始日後L長的子段是一個公共的子段,那麼從k開始日後數1,2,...,L-1長的子段都是公共的子段;若是第k個位置開始日後L長的子段是一個公共的子段,但第k個位置開始日後L+1長的子段不是一個公共的子段,那麼位置k到位置k+L中任一位置j開始日後直到位置k+L的子段都不是公共的子段。這就意味着公共的子段被劃分紅了若干個部分,每一個部分必定有最長的一個公共子段。對於一個最長的公共子段,不妨設其長度爲L,則與它劃分在同一組內的公共子段也就是它的子段,長度爲1的有L個,長度爲2的有L-1個…… 因而這一組一共有1+2+...+L=(L+1)*L/2個公共子段。blog
用一個數組pos[x][i]=j表示數字x在第i個排列中是第j個。要判斷第k個位置的數是否還跟前面是在同一組,就須要判斷前面那一組的開始(設爲第p個位置)處的數和第k個位置處的數在m個排列中的相對位置是否都同樣,便是不是都相差k-p,作一次檢查須要O(m)。而因爲公共子段的劃分是不重合的(即沒有一個公共子段屬於一個以上的組),因而只須要從前日後掃一遍:從i開始向後擴展公共子段,當新的位置再也不屬於前一個組時,起始位置i跳到這個新的位置繼續重複以前的操做。因而總的複雜度爲O(n*m)。get
(智障的zyy在比賽的時候把上一段加粗處的地方寫錯了,直接把位置當作這個位置上的數那來算,居然還過了6組數據orz…… 由於這個智障的問題,再一次跟跑回expert失之交臂…… (年輕時候的zyy真厲害啊…… (說不定這道題作對了也回不了expert呢orz…… (閉嘴吧……string
【個人實現】it
複雜度:O(n*m)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 6 using namespace std; 7 #define MaxN 100020 8 #define MaxM 20 9 10 long long pos[MaxN][MaxM]; 11 long long a[MaxN]; 12 long long Len[MaxN]; 13 int n, m; 14 bool Check(int x, int y) //true: same 15 { 16 int delta = pos[x][1] - pos[y][1]; 17 for(int i = 2; i <= m; i++) 18 if(pos[x][i] - pos[y][i] != delta) 19 return false; 20 return true; 21 } 22 23 int main() 24 { 25 int i, j; 26 int x; 27 long long Ans; 28 scanf("%d%d", &n, &m); 29 for(i = 1; i <= m; i++) 30 { 31 for(j = 1; j <= n; j++) 32 { 33 scanf("%d", &x); 34 pos[x][i] = j; //x zai i hang j lie 35 if(i == 1) 36 a[j] = x; 37 } 38 } 39 memset(Len, 0, sizeof(Len)); 40 for(i = 1; i <= n; ) 41 { 42 Len[i]++; 43 for(j = i+1; j <= n; j++) 44 { 45 if(Check(a[i], a[j])) 46 Len[i]++; 47 else 48 { 49 //i = j; 50 break; 51 } 52 } 53 i = j; 54 } 55 Ans = 0; 56 for(i = 1; i <= n; i++) 57 if(Len[i]) 58 Ans += Len[i] * (Len[i] + 1) / 2; 59 cout << Ans << endl; 60 return 0; 61 }
【評測結果】