暂无链接
B君的第六题
【问题描述】
丢掉幻想,准备斗争!
输入四个长度为nnn的序列ai,bi,ci,di。a_i, b_i, c_i, d_i。ai,bi,ci,di。
求这444序列最长公共子序列的长度。
公共子序列不需要连续。
【输入格式】
第一行一个整数nnn。
第二行n个整数表示序列aia_iai。
第三行n个整数表示序列bib_ibi。
第四行n个整数表示序列cic_ici。
第五行n个整数表示序列did_idi。
【输出格式】
一行一个整数,表示答案。
【输入样例】
5
1 2 1 2 3
1 2 3 1 2
3 1 2 1 2
1 2 1 2 1
【输出样例】
4
【数据范围】
对于100%100\%100%的数据,满足1≤n≤100001 ≤ n ≤ 100001≤n≤10000。
对于100%100\%100%的数据,1≤ai≤n,1≤bi≤n,1≤ci≤n,1≤di≤n1 ≤ a_i ≤ n,1 ≤ b_i ≤ n,1 ≤ c_i ≤ n,1 ≤ d_i ≤ n1≤ai≤n,1≤bi≤n,1≤ci≤n,1≤di≤n。
对于100%100\%100%的数据,在序列aia_iai中,任何数字出现次数都 ≤2≤ 2≤2次。
对于100%100\%100%的数据,在序列bib_ibi中,任何数字出现次数都 ≤2≤ 2≤2次。
对于100%100\%100%的数据,在序列cic_ici中,任何数字出现次数都$ ≤ 2$次。
对于30%30\%30%的数据,n≤50n ≤ 50n≤50。
对于另40%40\%40%的数据,ai,bi,ci,dia_i, b_i, c_i, d_iai,bi,ci,di为四个1,2,......,n1, 2, ......, n1,2,......,n的排列。
题解
写完O(n4)dpO(n^4)dpO(n4)dp后本来有个707070分的梦想,觉得排列肯定有妙妙的性质,两两求出最长公共子序列以后取minminmin就行了,写完了O(6nlog2n)O(6nlog_2n)O(6nlog2n)以后,随便randrandrand了组数据就把自己卡WAWAWA了。。。
梦想破灭,我太naivenaivenaive了。。。
先讲讲求两个字符串的最长公共子序列的方法,一般情况下是O(n2)O(n^2)O(n2)的,但是我们有妙妙的性质啊,题目保证每个数出现的次数为常数范围,我们就可以将一个串中的数字替换为该数字在另一个串里的出现位置的倒序序列,举个例子:
1 3 4 4 6 54 5 1 9 3 61\ 3\ 4\ 4\ 6\ 5\\4\ 5\ 1\ 9\ 3\ 61 3 4 4 6 54 5 1 9 3 6
我们可以将下面的串替换为:
(4 3)(6)(1)()(2)(5)(4\ 3)(6)(1)()(2)(5)(4 3)(6)(1)()(2)(5)
这样,替换后的串的最长上升子序列就是原串的最长公共子序列,求解复杂度为O(nlog2n)O(nlog_2n)O(nlog2n)。
我们用相同的方法,用did_idi替换ai,bi,cia_i,b_i,c_iai,bi,ci,问题就转化成了在三个序列中求最长公共不上升子序列,我们将对应位置上的数字拆分出的序列三三组合看做一个三维的物品,那么问题就变成了四维偏序。
代码
#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();}