XJTUOJ #1325. [L2-4]orzczq

题目

https://oj.xjtuicpc.com/problem/1325
PS:原题

思路

先考虑一个简化版的问题,如果给出的两个序列都是1~n的排列该怎么解决。

画个图理解一下,相同的数珂以上下配对,我们连一条线。那么问题就是要求最多选出几条不相交的线段。简单观察一下就发现实质上是要让线段的下端点单调递增。

我们把第一行每个数对应的下端点的位置标出来:2,5,1,3,4,不妨记为c数组,那么问题就成功转化为了c数组的LIS(最长上升子序列),而LIS是有\(\Theta(nlogn)\)做法的,

利用单调性二分或直接用线段树维护DP最值即可,然后这个简化题就做完了。

考虑本题与简化版本的区别,发现棘手之处就在于相同的数有5个,导致我无法找到一个一一对应的关系。然后我这个菜鸡瞎猜了个结论,钦定相同的数前对前,后对后,

然后WA了,然后就弃疗打了个\(n^2\)朴素DP,水了50pt滚粗了。

丢人,滚去贺标算,看了一眼大佬的标程,发现有一种很妙的处理方式。

由于事实上相同数的最优对应方案是不确定的,所以我们只能把所有的对应方式都考虑上,也就是一个上端点连5个下端点,也就是说,原本的c数组的每个元素对应一个5元组。

这时候就增加了一个额外限制,5元组中只能取一个,也不是不能做,但好像很麻烦。

更简单的处理方法是将这个5元组从大到小排,这就使得求LIS时同一组的元素不可能相接。

代码

点击查看代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define maxn 20010
#define inf 0x3f3f3f3f
using namespace std;
int a[maxn*5],b[maxn*5],c[maxn*25];
int p[maxn][6],dp[maxn*25],len[maxn*25];
int cnt[maxn];
int main(){
    int n,i,j;
    int ans=0;
    scanf("%d",&n);
    for(i=1;i<=5*n;++i) scanf("%d",&a[i]);
    for(i=1;i<=5*n;++i) scanf("%d",&b[i]);
    for(i=1;i<=5*n;++i){
        p[b[i]][++cnt[b[i]]]=i;
    }
    for(i=1;i<=5*n;++i){
        for(j=1;j<=5;++j){
            c[(i-1)*5+(6-j)]=p[a[i]][j];
        }
    }
    for(i=1;i<=25*n;++i) len[i]=inf;
    for(i=1;i<=25*n;++i){
        int k=lower_bound(len,len+25*n+1,c[i])-len-1;
        dp[i]=k+1;
        len[k+1]=min(c[i],len[k+1]);
        ans=max(ans,dp[i]);
    }
    printf("%d",ans);
    // system("pause");
    return 0;
}
posted @ 2022-04-12 17:01  文艺平衡树  阅读(30)  评论(0编辑  收藏  举报