BZOJ3439: Kpm的MC密码

BZOJ3439: Kpm的MC密码

Tire树·dfs序·主席树

题解:

把字符串反过来,后缀变成前缀,扔进Tire树里。

以一个字符串结束点为根的子树中的单词都是它的Kpm串。要求其中第K大的编号。

求Tire树的dfs序,子树变成连续的区间,套主席树的区间第K大。

注意:有相同的字符串。

不仅Tire树结束标记要用vector了,而且主席树插入的时候也不能直接clone上一层的了(那样cnt就不对了),而是本层第一个先clone上一层,后面都clone这一层。另外,如果这一层没有,不要忘了root[i]=root[i-1].

一开始以为本身不算,写了个rank处理了一下,后来发现不用。。。

道理都懂,然而还是写了1.5h+

看着这些注释的调试输出,你能感受到我的绝望吗QWQ

Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
const int N = 100005;

int n,m; char str[N];
int pos[N],ptr[N],end[N],tim,root[N];
vector<int> lab[N]; int relib[N];

namespace Tire{
    int ch[N][26]; int sz=1;
    void rev(char s[]){
        int len=strlen(s);
        for(int i=0;i<len/2;i++) swap(s[i],s[len-i-1]);
    }
    void insert(char s[],int id){
        int x=1;
        for(int i=0;s[i];i++){
            if(!ch[x][s[i]-'a']) ch[x][s[i]-'a']=++sz;
            x=ch[x][s[i]-'a'];
        }
        lab[x].push_back(id); relib[id]=x;
    }
    void dfs(int x){
        pos[x]=++tim; ptr[pos[x]]=x;
        for(int i=0;i<26;i++) if(ch[x][i]) {
            dfs(ch[x][i]);
        }
        end[x]=tim;
    }
}

namespace Tree{
    int sz,lch[N*20],rch[N*20],cnt[N*20];
    void clone(int x,int t){
        lch[x]=lch[t]; rch[x]=rch[t]; cnt[x]=cnt[t];
    }
    void insert(int &x,int t,int l,int r,int p){
//      D(l); D(r); D(p); E;    
        x=++sz; clone(x,t);
        cnt[x]++; 
//      D(cnt[x]); E;
        if(l!=r){
            int mid=(l+r)>>1;
            if(p<=mid) insert(lch[x],lch[t],l,mid,p);
            else insert(rch[x],rch[t],mid+1,r,p);
        }
    }
//  int rank(int a,int b,int l,int r,int p){
//      if(l==r) return 1;
//      int mid=(l+r)>1;
//      if(p<=mid) return rank(lch[a],lch[b],l,mid,p);
//      else return rank(rch[a],rch[b],mid+1,r,p) + cnt[lch[b]]-cnt[lch[a]];
//  }
    int query(int a,int b,int l,int r,int k){
//      D(a); D(b); D(l); D(r); D(k); E;
        if(cnt[b]-cnt[a] < k) return -1;
        if(l==r) return l;
        int mid=(l+r)>>1;
//      D(lch[b]); D(lch[a]); E;
        int lsz=cnt[lch[b]]-cnt[lch[a]]; 
//      D(cnt[lch[b]]); D(cnt[lch[a]]); D(lsz); E;
        if(k<=lsz) return query(lch[a],lch[b],l,mid,k);
        else return query(rch[a],rch[b],mid+1,r,k-lsz);
    }
}



int main(){
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",str);
        Tire::rev(str); 
        Tire::insert(str,i);
    }
    Tire::dfs(1);
//  for(int i=1;i<=Tire::sz;i++){
//      D(pos[i]); D(end[i]); E;
//  }
    for(int i=1;i<=tim;i++){
//      D(i); E;
        if(lab[ptr[i]].size()){
            int pre=root[i-1];
            for(int j=0;j<lab[ptr[i]].size();j++){
                Tree::insert(root[i],pre,1,n,lab[ptr[i]][j]); 
                pre=root[i];
//              D(lab[ptr[i]][j]); E;
            }
        }
        else root[i]=root[i-1];
    }
//  D(Tree::query(root[4],root[5],1,n,1)); E;
    for(int i=1;i<=n;i++){
        int x=relib[i];
        int k; scanf("%d",&k);
//      int rk=Tree::rank(root[pos[x]-1],root[end[x]],1,n,i);
//      D(rk); E;
//      if(rk<=k) k++;
//      D(pos[x]); D(end[x]); E;
        int ans=Tree::query(root[pos[x]-1],root[end[x]],1,n,k);
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值