【JZOJ5573】子序列

Description

这里写图片描述

Solution

考虑现在已经找到字典序第 i 小的子序列,设它为 Sab S 为前面的序列),它的两种扩展为:
1. Sac c 为所在原序列位置在 a 之后第一个大于或等于 b 的数)
2. Sabd d 为位置在 b 之后最小的数)
如果对于这些序列设置一个比较函数,那么就是经典问题:将字典序最小的序列从堆中提出来,加入它的两个扩展到堆中。
我们发现序列长度较长不能保存,但其实子序列之间有很多前缀关系,我们可以使用一棵trie,通过trie上的节点来表示这些子序列。至于两个子序列的比较,可以通过它们的 lca 往下一个儿子的大小关系来比较。
堆或优先队列复杂度 O(log2n) ,子序列比较 O(log2n) ,所以总复杂度是 O(klog22n) 的。
但这个方法常数较大,且不易实现。

于是我们不机械的把所有可能的子序列都丢进堆里,靠比较来得出序列,我们可以直接构造。

设答案(即最终序列集)为 [1,k] ,对于一段哈希值相同的序列集区间 [l,r] ,它一定是由同样 [l,r](r<l) 中的序列(区间中哈希值相同)直接构造出来的。
考虑对于一段序列集 [l,r] ,如何构造字典序最小且刚好能接在已有的序列集后。
posx 表示 x 这个序列的结尾在原序列的位置。
枚举 posl 后严格第 i 大的数字num,找到 posl 后所有等于 num 的数字,每一个都尝试接在 [l,r] 后面,就会得到一个新的 [l,r] ,且保证结尾位置递增。这样显然是符合字典序的。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N],tot=0;
struct tree{
    int l,r,x,p;
}tr[N*20];
int rt[N],nx[N],mx=0;
int wz[N];
int n,K,seed,mo;
void insert(int &v,int l,int r,int x){
    tr[++tot]=tr[v],v=tot;
    if(l==r) {tr[v].p=x,tr[v].x++;return;}
    int mid=(l+r)>>1;
    a[x]<=mid?insert(tr[v].l,l,mid,x):insert(tr[v].r,mid+1,r,x);
    tr[v].x=tr[tr[v].l].x+tr[tr[v].r].x;
}
int find(int v,int l,int r,int x){
    if(x>tr[v].x) return 0;
    if(l==r) return tr[v].p;
    int mid=(l+r)>>1;
    return tr[tr[v].l].x<x?find(tr[v].r,mid+1,r,x-tr[tr[v].l].x):find(tr[v].l,l,mid,x);
}
struct node{
    int p;
    ll hs;
}b[N];
int tt=0;
void dfs(int l,int r){
    int o=b[l].p;
    fo(i,1,tr[rt[o+1]].x){
        int p=find(rt[o+1],1,mx,i),nw=tt,ww=0;
        for(int j=p;j;j=nx[j]){
            ww++;
            fo(k,l,r)
            if(j>b[k].p){
                b[++tt].p=j,b[tt].hs=(b[k].hs*seed+a[j])%mo;
                if(tt==K){
                    fo(ch,1,K) printf("%lld\n",b[ch].hs);
                    exit(0);
                }
            }
        }
        dfs(nw+1,tt),i+=ww-1;
    }
}
int main()
{
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d %d %d %d",&n,&K,&seed,&mo);
    fo(i,1,n){
        scanf("%d",&a[i]),mx=max(mx,a[i]);
        nx[wz[a[i]]]=i,wz[a[i]]=i;
    }
    nx[0]=0;
    fd(i,n,1)
    rt[i]=rt[i+1],insert(rt[i],1,mx,i);
    dfs(0,0);
}

猜你喜欢

转载自blog.csdn.net/sadnohappy/article/details/79511312
今日推荐