[2018.07.14 T3] B君的第六题

40 篇文章 0 订阅
5 篇文章 0 订阅

暂无链接

B君的第六题

【问题描述】

丢掉幻想,准备斗争!

输入四个长度为 n n 的序列ai,bi,ci,di

求这 4 4 序列最长公共子序列的长度。

公共子序列不需要连续。

【输入格式】

第一行一个整数n

第二行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 % 的数据,满足 1n10000 1 ≤ n ≤ 10000

对于 100% 100 % 的数据, 1ain1bin1cin1din 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 % 的数据, n50 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 ) 的,但是我们有妙妙的性质啊,题目保证每个数出现的次数为常数范围,我们就可以将一个串中的数字替换为该数字在另一个串里的出现位置的倒序序列,举个例子:

1 3 4 4 6 54 5 1 9 3 6 1   3   4   4   6   5 4   5   1   9   3   6

我们可以将下面的串替换为:

(4 3)(6)(1)()(2)(5) ( 4   3 ) ( 6 ) ( 1 ) ( ) ( 2 ) ( 5 )

这样,替换后的串的最长上升子序列就是原串的最长公共子序列,求解复杂度为 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();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值