P3975 [TJOI2015]弦论

思路

一眼SAM板子,结果敲了一中午。。。
我还是太弱了
题目要求求第k小的子串
我们可以把t=0当成t=1的特殊情况,(所有不同位置的相同子串算作一个就是相当于把所有子串的出现位置个数(endpos大小)全部赋成1)
然后讨论如何递推的求第k小的子串
首先要统计一个sum值,代表从这个状态出发能够到达多少个子串,相当于这个节点后继节点的所有值的和,反向拓扑一下能到那些节点即可
然后逐个dfs每个位置的字符是什么即可

注意

反向拓扑不一定要真的反向建边(狗头),我这么乱搞了。。。
可以把每个点的maxlen从小到大基数排序一发,然后正向拓扑就是从小到大,反向就是从大到小
会快很多

因为乱搞只能开O2过的代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define int long long 
const int MAXN = 500100*2;
using namespace std;
int trans[MAXN][26],suflink[MAXN],endpos[MAXN],maxlen[MAXN],minlen[MAXN],Nodecnt,ispre[MAXN],in[MAXN],sum[MAXN];
char s[MAXN],ans[MAXN];
int new_state(int _maxlen,int _minlen,int *_trans,int _suflink){
    ++Nodecnt;
    maxlen[Nodecnt]=_maxlen;
    minlen[Nodecnt]=_minlen;
    if(_trans)
        for(int i=0;i<26;i++)
            trans[Nodecnt][i]=_trans[i];
    suflink[Nodecnt]=_suflink;
    return Nodecnt;
}
int add_len(int u,int c){
    int z=new_state(maxlen[u]+1,0,NULL,0);
    ispre[z]=1;
    while(u&&trans[u][c]==0){
        trans[u][c]=z;
        u=suflink[u];
    }
    if(!u){
        suflink[z]=1;
        minlen[z]=1;
        return z;
    }
    int v=trans[u][c];
    if(maxlen[v]==maxlen[u]+1){
        suflink[z]=v;
        minlen[z]=maxlen[v]+1;
        return z;
    }
    int y=new_state(maxlen[u]+1,0,trans[v],suflink[v]);
    suflink[v]=suflink[z]=y;
    minlen[v]=minlen[z]=maxlen[y]+1;
    while(u&&trans[u][c]==v){
        trans[u][c]=y;
        u=suflink[u];
    }
    minlen[y]=maxlen[suflink[y]]+1;
    return z;
}
queue<int> q;
void topu_size(int tx){
    for(int i=1;i<=Nodecnt;i++) 
        in[suflink[i]]++;
    for(int i=1;i<=Nodecnt;i++)
        if(!in[i])
            q.push(i);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        endpos[x]+=ispre[x];
        endpos[suflink[x]]+=endpos[x];
        in[suflink[x]]--;
        if(!in[suflink[x]])
            q.push(suflink[x]);
    }
}
int u[MAXN*10],v[MAXN*10],fir[MAXN*10],nxt[MAXN*10],cnt;
void addedge(int ui,int vi){
    ++cnt;
    u[cnt]=ui;
    v[cnt]=vi;
    nxt[cnt]=fir[ui];
    in[vi]++;
    fir[ui]=cnt;
}
void topu_sum(int tx){
    for(int i=1;i<=Nodecnt;i++){
        for(int j=0;j<26;j++)
            if(trans[i][j]){
                addedge(trans[i][j],i);
            }
        if(tx==0){
            endpos[i]=sum[i]=1;
        }
        else{
            sum[i]=endpos[i];
        }
    }
    sum[1]=endpos[1]=0;
    for(int i=1;i<=Nodecnt;i++)
        if(!in[i])
            q.push(i);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=fir[x];i;i=nxt[i]){
            sum[v[i]]+=sum[x];
            in[v[i]]--;
            if(!in[v[i]])
                q.push(v[i]);
        }
    }
}
int k,t,n;
int dfs(int pos,int x,int tx){
    // printf("pos=%lld x=%lld k=%lld\n",pos,x,k);
    if(k<=endpos[x])
        return 1;
    k-=endpos[x];
    for(int i=0;i<26;i++){
        if(!trans[x][i])
            continue;
        // printf("gg %d\n",trans[x][i]);
        int mid=sum[trans[x][i]];
        if(k>mid)
            k-=mid;
        else{
            // printf("to %d\n",trans[x][i]);
            // k-=endpos[trans[x][i]];
            ans[pos]='a'+i;
            return dfs(pos+1,trans[x][i],tx);
        }
    }
    return -1;
}
signed main(){
    freopen("1.in","r",stdin);
    scanf("%s",s+1);
    scanf("%lld %lld",&t,&k);
    n=strlen(s+1);
    Nodecnt=1;
    int pre=1;
    for(int i=1;i<=n;i++)
        pre=add_len(pre,s[i]-'a');
    // printf("ok\n");
    topu_size(t);
    topu_sum(t);
    // for(int i=1;i<=Nodecnt;i++)
    //     printf("maxlen=%lld minlen=%lld endpos=%lld suflink=%lld sum=%lld\n",maxlen[i],minlen[i],endpos[i],suflink[i],sum[i]);
    int req=dfs(1,1,t);
    if(req==-1){
        printf("-1\n");
        return 0;
    }
    printf("%s\n",ans+1);
    return 0;
}

posted @ 2019-03-12 14:25  dreagonm  阅读(193)  评论(0编辑  收藏  举报