题意就是给你两个自身不会有重复元素的数列,求最长公共子序列,O(nlogn)。
去年(?)做过一遍的题现在想想还是很巧妙。
想一想,枚举B的数B[i],看它在A的哪里(丢进桶里维护)。如果有,怎么计算它的答案。
如果有的话,对于一个B[j](j<i),可以转移到它,就是要有:
即找到一个桶的前缀最大值,并进行单点修改,且只会修改一次,且只会增加。
这个就可以用值域树状数组/值域线段树做了。
相当于一个映射后的最长上升子序列,但我每次写这个东西也是用的值域树状数组/值域线段树……懒得想二分。
最重要的性质就是不会有重复的元素,因为有重桶就不能用了。
写起来也很舒服的。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define dob double #define lb(x) (x&-x) #define FILE "10635" using namespace std; const int N = 70010; int n,p,q,bin[N],T[N],Ans; inline int gi(){ int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline int query(int x,int res=0){ for(;x;x-=lb(x))res=max(res,T[x]); return res; } inline void update(int x,int val){ for(;x<=n*n;x+=lb(x))T[x]=max(T[x],val); } inline void solve(){ n=gi();p=gi()+1;q=gi()+1;Ans=0; memset(bin,0,sizeof(bin));memset(T,0,sizeof(T)); for(int i=1;i<=p;++i)bin[gi()]=i; for(int i=1;i<=q;++i){ int y=bin[gi()];if(!y)continue; int v=query(y)+1;if(v>Ans)Ans=v; update(y,v); } printf("%d\n",Ans); } int main() { freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); int Case=gi(); for(int t=1;t<=Case;++t) printf("Case %d: ",t),solve(); fclose(stdin);fclose(stdout); return 0; }