[2018.07.14 T3] B君的第六题

探讨了在四个序列中寻找最长公共子序列的问题,利用动态规划和优化技巧,提出了高效的解决方案。通过将序列转换为特定形式,将问题转化为求最长不上升子序列,最终实现O(nlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂无链接

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 ≤ 100001n10000

对于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 ≤ n1ain1bin1cin1din

对于100%100\%100%的数据,在序列aia_iai中,任何数字出现次数都 ≤2≤ 22次。

对于100%100\%100%的数据,在序列bib_ibi中,任何数字出现次数都 ≤2≤ 22次。

对于100%100\%100%的数据,在序列cic_ici中,任何数字出现次数都$ ≤ 2$次。

对于30%30\%30%的数据,n≤50n ≤ 50n50

对于另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();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShadyPi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值