暂无链接
B君的第六题
【问题描述】
丢掉幻想,准备斗争!
输入四个长度为 n n 的序列
求这 4 4 序列最长公共子序列的长度。
公共子序列不需要连续。
【输入格式】
第一行一个整数。
第二行n个整数表示序列 ai a i 。
第三行n个整数表示序列 bi b i 。
第四行n个整数表示序列 ci c i 。
第五行n个整数表示序列 di d i 。
【输出格式】
一行一个整数,表示答案。
【输入样例】
5
1 2 1 2 3
1 2 3 1 2
3 1 2 1 2
1 2 1 2 1
【输出样例】
4
【数据范围】
对于 100% 100 % 的数据,满足 1≤n≤10000 1 ≤ n ≤ 10000 。
对于 100% 100 % 的数据, 1≤ai≤n,1≤bi≤n,1≤ci≤n,1≤di≤n 1 ≤ a i ≤ n , 1 ≤ b i ≤ n , 1 ≤ c i ≤ n , 1 ≤ d i ≤ n 。
对于 100% 100 % 的数据,在序列 ai a i 中,任何数字出现次数都 ≤2 ≤ 2 次。
对于 100% 100 % 的数据,在序列 bi b i 中,任何数字出现次数都 ≤2 ≤ 2 次。
对于 100% 100 % 的数据,在序列 ci c i 中,任何数字出现次数都 ≤2 ≤ 2 次。
对于 30% 30 % 的数据, n≤50 n ≤ 50 。
对于另 40% 40 % 的数据, ai,bi,ci,di a i , b i , c i , d i 为四个 1,2,......,n 1 , 2 , . . . . . . , n 的排列。
题解
写完 O(n4)dp O ( n 4 ) d p 后本来有个 70 70 分的梦想,觉得排列肯定有妙妙的性质,两两求出最长公共子序列以后取 min m i n 就行了,写完了 O(6nlog2n) O ( 6 n l o g 2 n ) 以后,随便 rand r a n d 了组数据就把自己卡 WA W A 了。。。
梦想破灭,我太 naive n a i v e 了。。。
先讲讲求两个字符串的最长公共子序列的方法,一般情况下是 O(n2) O ( n 2 ) 的,但是我们有妙妙的性质啊,题目保证每个数出现的次数为常数范围,我们就可以将一个串中的数字替换为该数字在另一个串里的出现位置的倒序序列,举个例子:
我们可以将下面的串替换为:
这样,替换后的串的最长上升子序列就是原串的最长公共子序列,求解复杂度为 O(nlog2n) O ( n l o g 2 n ) 。
我们用相同的方法,用 di d i 替换 ai,bi,ci a i , b i , c i ,问题就转化成了在三个序列中求最长公共不上升子序列,我们将对应位置上的数字拆分出的序列三三组合看做一个三维的物品,那么问题就变成了四维偏序。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
struct sd{int op,id,a,b,c;}ope[4][M];
bool cmp1(sd a,sd b){return a.a!=b.a?a.a<b.a:(a.b!=b.b?a.b>b.b:a.c>b.c);}
bool cmp2(sd a,sd b){return a.b!=b.b?a.b<b.b:a.c>b.c;}
int a[M],b[M],c[M],d[M],mx[M],len[M],n,tot;
vector<int>pa[M],pb[M],pc[M];
void in()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),pa[a[i]].push_back(i);
for(int i=1;i<=n;++i)scanf("%d",&b[i]),pb[b[i]].push_back(i);
for(int i=1;i<=n;++i)scanf("%d",&c[i]),pc[c[i]].push_back(i);
for(int i=1;i<=n;++i)
{
scanf("%d",&d[i]);
for(int ja=pa[d[i]].size()-1;ja>=0;--ja)for(int jb=pb[d[i]].size()-1;jb>=0;--jb)for(int jc=pc[d[i]].size()-1;jc>=0;--jc)
ope[0][++tot]=(sd){0,tot,pa[d[i]][ja],pb[d[i]][jb],pc[d[i]][jc]};
}
}
#define lb(x) (x&-x)
void clear(int x){for(;x<M;x+=lb(x))mx[x]=0;}
void add(int x,int d){for(;x<M;x+=lb(x))mx[x]=max(mx[x],d);}
int ask(int x){int ans=0;for(;x;x-=lb(x))ans=max(ans,mx[x]);return ans;}
void solve(int cot)
{
for(int i=1;i<=cot;++i)
{
if(ope[2][i].op)len[ope[2][i].id]=max(len[ope[2][i].id],ask(ope[2][i].c-1)+1);
else add(ope[2][i].c,len[ope[2][i].id]);
}
for(int i=1;i<=cot;++i)if(!ope[2][i].op)clear(ope[2][i].c);
}
void cdq(int v,int le,int ri)
{
if(le==ri)return;int mid=le+ri>>1,cot=0;cdq(v,le,mid);
for(int i=le;i<=mid;++i)
{
if(v==1)ope[v][++cot]=ope[v-1][i],ope[v][cot].op=0;
else if(!ope[v-1][i].op)ope[v][++cot]=ope[v-1][i];
}
for(int i=mid+1;i<=ri;++i)
{
if(v==1)ope[v][++cot]=ope[v-1][i],ope[v][cot].op=1;
else if(ope[v-1][i].op)ope[v][++cot]=ope[v-1][i];
}
if(v==1)sort(ope[v]+1,ope[v]+1+cot,cmp1);else sort(ope[v]+1,ope[v]+1+cot,cmp2);
if(v==1)cdq(v+1,1,cot);else solve(cot);
cdq(v,mid+1,ri);
}
void ac()
{
for(int i=1;i<=tot;++i)len[i]=1;
cdq(1,1,tot);int ans=0;
for(int i=1;i<=tot;++i)ans=max(ans,len[i]);printf("%d",ans);
}
int main(){in();ac();}