今天才知道,原来lcs也有O(nlogn)的做法……
首先对于两个排列(即使不是排列,离散化一下就行),我们能很容易的得出a[i]在b中出现的位置,但是无法保证出现的相对位置和a[i]相同,所以我们要解决的是求出a[i]在b中出现的相对位置相同的最长序列长度是多少。(相对位置就是对于两个的数x, y,在a中出现的位置为xa,ya,b中出现的位置为xb, yb,让xa, ya和xb, yb的大小关系相同就是相对位置相同)
那么我们对a数组中的每一个数按位置标号,然后对应到b中,那么此时b中的每一个数代表这个数在a中什么时候出现的,a中的出现位置是单调递增的,那么我们要找的就是在b中也单调递增的一个序列,于是就转化成了最长上升子序列。
举个栗子:a = 3, 2, 1, 4 , 5.b = 1, 2, 3, 4, 5。于是3的出现位置是1,2是2,1->3, 4->4, 5->5。那么对于b中的每一个数,映射到出现位置上:b = 3, 2, 1, 4, 5。对这个求一遍LIS,答案是3.
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 3e5 + 5; 21 const int max_seq = 3e3 + 5; 22 inline ll read() 23 { 24 ll ans = 0; 25 char ch = getchar(), last = ' '; 26 while(!isdigit(ch)) {last = ch; ch = getchar();} 27 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 28 if(last == '-') ans = -ans; 29 return ans; 30 } 31 inline void write(ll x) 32 { 33 if(x < 0) x = -x, putchar('-'); 34 if(x >= 10) write(x / 10); 35 putchar(x % 10 + '0'); 36 } 37 38 int n, m, a[maxn], b[maxn], t[maxn]; 39 int dp[maxn], g[maxn]; 40 int main() 41 { 42 n = read(); 43 for(int i = 1; i <= n; ++i) a[i] = read(); 44 for(int i = 1; i <= n; ++i) b[i] = read(); 45 for(int i = 1; i <= n; ++i) t[a[i]] = i; //映射到位置上 46 for(int i = 1; i <= n; ++i) b[i] = t[b[i]]; 47 Mem(g, 0x3f); 48 for(int i = 1; i <= n; ++i) //求LIS 49 { 50 int x = upper_bound(g + 1, g + n + 1, b[i]) - g; 51 g[x] = b[i]; 52 dp[i] = x; 53 } 54 int ans = 0; 55 for(int i = 1; i <= n; ++i) ans = max(ans, dp[i]); 56 write(ans); enter; 57 return 0; 58 }