1. 程式人生 > >2017-5-14 湘潭市賽 Longest Common Subsequence 想法題

2017-5-14 湘潭市賽 Longest Common Subsequence 想法題

pre ati 個數 離散 code output iterator n) ret

Longest Common Subsequence
Accepted : 7           Submit : 66
Time Limit : 3000 MS           Memory Limit : 65536 KB

Longest Common Subsequence

Bobo has a sequence A=(a1,a2,…,an) of length n. He would like to know f(0),f(1),f(2) and f(3) where f(k) denotes the number of integer sequences X=(x1,x2,x3) where
: 1≤x1,x2,x3≤m; The length of longest common subsequence of A and X is exactly k. Note: u is a subsequence of v if and only if u can be obtained by removing some of the entries from v (possibly none). u is common subsequence of v and w if and only if u is subsequence of v and w. Input The input contains zero or more test cases and
is terminated by end-of-file. For each case, The first line contains two integers n,m. The second line contains n integers a1,a2,…,an. 1≤n≤200 1≤m,a1,a2,…,an≤106 The number of tests cases does not exceed 10. Output For each case, output four integers which denote f(0),f(1),f(2),f(3). Sample Input
3 3 1 2 2 5 3 1 2 3 2 1 Sample Output 1 14 11 1 0 1 17 9 Note For the second sample, X=(3,3,3) is the only sequence that the length of longest common subsequence of A and X is 1. Thus, f(1)=1. Source XTU OnlineJudge /** 題目:Longest Common Subsequence 鏈接:http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1265 題意:給定序列A,含n個數。和一個數m; 有一個X序列{x1,x2,x3},x1,x2,x3來自[1,m]區間內的數。 設:f(k)表示兩個序列的最長公共子序列為k; 求序列X和A滿足f(0),f(1),f(2),f(3)的X各有多少種。 思路:f(0)易求。計算出n個數中在m範圍內的不同數的數量為cnt,然後計算(m-cnt)^3即可。 對n個數中<=m的數,離散化。 要求f(1),f(2),f(3); f(3)的X序列內的數一定是n個數中獲得。 f(2)的X序列內的數有兩種來源,一種是三個數從n個數中獲得,然後Longest Common Subsequence為2; 另一種是其中兩個是n個數中獲得,然後Longest Common Subsequence為2,另外一個是<=m的不包含n個數的數中獲得。 f(1)的X序列內的數有三種來源,一種是3個數從n個數中獲得,然後Longest Common Subsequence為1; 另一種是兩個數從n個數中獲得,然後Longest Common Subsequence為1;另外一個是是<=m的不包含n個數的數中獲得。 另一種是其中一個是n個數中獲得,然後Longest Common Subsequence為1,另外二個是<=m的不包含n個數的數中獲得。 設:cnt表示n個數中<=m的數。kind表示n個數中<=m的不同數的種類。m-kind表示不包含n個數的數的種數。 枚舉x1,x2,x3全是n個數組成的X序列,所有xi都枚舉所有的n個數。 這些序列包含f(3),f(2)第一種來源,f(1)第一種來源的情況數。計算方法:一種是常規的Longest Common Subsequence 的dp求法,但是總時間復雜度為O(N^4); 更好的做法是:預處理next[i][j]表示i這個位置右邊第一次出現j這個數的位置。那麽可以O(1)判斷X序列的Longest Common Subsequence為k。把它們更新到f(k)中。 flag[a[i]][a[j]][a[k]]表示這個序列可以匹配的最大LCS;取最值,最後遍歷計算即可。 剩下要求f(2)的第二種來源,f(1)的第二種來源和第三種來源。 f(2)第二種來源:三種可能(x1,x2),(x2,x3),(x1,x3)位置,Longest Common Subsequence為2,另一個位置為m-kind中取。 其實情況數是一樣的。f(2) += 3*Q(cnt)*(m-kind); Q(cnt)表示cnt個數中找到與A序列的Longest Common Subsequence為2的序列數量。暴力枚舉兩個位置,同上面說的方法。 f(1)第二種來源:三種可能(x1,x2),(x2,x3),(x1,x3)位置,但是Longest Common Subsequence為1;另外一個位置從m-kind中取。 f(1) += 3*P(cnt)*(m-kind); f(1)第三種來源:三種可能x1,x2,x3位置相同,另外兩個位置為m-kind中取。f(1) += 3*kind*(m-kind)*(m-kind); 思考:為什麽要用next[i][j]表示i這個位置右邊第一次出現j這個數的位置。而不是直接next[i][j]表示j這個數是否在i的右邊。 假設: 2 2 那麽枚舉出來2 2 2. 如果用後者所述定義,那麽答案就是3了。實際上只能是2. */ #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> P; const int maxn = 1e5+100; int flag[202][202][202]; int sign[202][202]; int mark[1000005]; int n, m; int cnt , kind; LL f[4]; int a[202], z; int Next[202][202]; set<int> se; void NextInit() { for(int i = 0; i < z; i++){ for(int j = i+1; j < z; j++){ if(Next[i][a[j]]==-1){ Next[i][a[j]] = j; } } } } void lisan() { int cnt = 1; set<int>::iterator it; for(it = se.begin(); it!=se.end(); it++){ mark[*it] = cnt; cnt++; } for(int i = 0; i < z; i++){ a[i] = mark[a[i]]; } } void init() { memset(f, 0, sizeof f); memset(Next, -1, sizeof Next); memset(flag, 0, sizeof flag); memset(sign, 0, sizeof sign); se.clear(); } int cal(int i,int j,int k) { if(Next[i][a[j]]==j&&Next[j][a[k]]==k) return 3; if(Next[i][a[j]]==j) return 2; if(Next[j][a[k]]==k) return 2; if(Next[i][a[k]]==k) return 2; return 1; } void solve() { kind = se.size(); cnt = z; LL temp = m-kind; f[0] = 1LL*temp*temp*temp; ///枚舉三層循環所有序列。 for(int i = 0; i < z; i++){ for(int j = 0; j < z; j++){ for(int k = 0; k < z; k++){ int len = cal(i,j,k); flag[a[i]][a[j]][a[k]] = max(flag[a[i]][a[j]][a[k]],len); } } } se.clear();///清空原先沒有離散的數據。 for(int i = 0; i < z; i++){ se.insert(a[i]); } set<int>::iterator it, it1, it2; for(it = se.begin(); it!=se.end(); it++){ for(it1 = se.begin(); it1!=se.end(); it1++){ for(it2 = se.begin(); it2!=se.end(); it2++){ f[flag[*it][*it1][*it2]]++; } } } ///枚舉兩層循環. int cnt2, cnt1; for(int i = 0; i < z; i++){ for(int j = 0; j < z; j++){ int len; if(Next[i][a[j]]==j) len = 2; else len = 1; sign[a[i]][a[j]]= max(sign[a[i]][a[j]],len); } } cnt1 = cnt2 = 0; for(it = se.begin(); it!=se.end(); it++){ for(it1 = se.begin(); it1!=se.end(); it1++){ if(sign[*it][*it1]==2){ cnt2++; }else cnt1++; } } f[2] += 1LL*3*cnt2*(m-kind); f[1] += 1LL*3*cnt1*(m-kind); ///f(1)第三種來源; f[1] += 1LL*3*kind*(m-kind)*(m-kind); } int main() { while(scanf("%d%d",&n,&m)==2) { z = 0; for(int i = 0; i < n; i++) { scanf("%d",&a[z]); if(a[z]<=m) z++; } init(); for(int i = 0; i < z; i++){ se.insert(a[i]); } lisan(); NextInit(); solve(); printf("%I64d %I64d %I64d %I64d\n",f[0], f[1], f[2], f[3]); } return 0; }

2017-5-14 湘潭市賽 Longest Common Subsequence 想法題