BZOJ-[SCOI2012]喵星球上的点名(后缀数组正解:Suffix Array+ST+Binary Search+BIT+sort)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2754

网上大神们的正解都是用AC自动机,蒟蒻不会写AC自动机,只能后缀数组硬着头皮上了。(网上绝大多数后缀数组解法都存在被卡TLE的可能(n^2暴力枚举串),找不到完整详细的解释跟代码,蒟蒻只能来发发解法秀秀下限了):

首先,就是把所有名字和姓和点名串串在一起(之间加一个不可能出现的数,如负无穷之类的),然后做一次后缀数组,求出height[],sa[],rank[],那么,以点名串为前缀的后缀在sa[]中必定是一段连续的区间出现,然后对于第1个问题,就转化为了查询区间中不同的数(同属一个姓名的后缀认为是同一个数)的个数的问题,第二个问题,就转化成了,某一个数被多少个不同的区间覆盖的问题,这两个问题我实在想不出在线解法,只能用离散化+BIT维护来写。

对于第一个问题:参见“BZOJ-1878: [SDOI2009]HH的项链”的解法,我就懒得码字了,来个大神们的题解:传送门(http://www.dxmtb.com/blog/diff/

对于第二个问题,首先,用类似基数排序的方法对区间进行离散化:

设数组t[]为基数排序中的桶,对于每一个区间[l,r],加入t[l],然后做标记A,再加入t[r+1],做标记B;

对于每一个后缀数组中的后缀i,如果该后缀属于某一个姓名,预处理出在该后缀前面的第一个属于同一个单词的后缀last[i],如果不存在,设last[i]=0。

然后,从1到L(后缀数组长度)扫一遍,如果当前在位置i,首先处理完t[i]里面加入的所有区间,设该区间为[l,r],若该区间被标记A标记,则在树状数组执行对l位置+1的操作,若被标记B标记,则在树状数组中执行对l位置-1的操作,然后如果该后缀属于某一姓名,则对于该姓名的答案+Sum(last[i]+1,i),直到整个数组处理完毕,这是所有答案也就处理完成了。

复杂度:

倍增算法求后缀数组:O(L log L)

离线处理问题一:O(n log n + L)

离线处理问题二:O(n log n +L)

总复杂度:O(L log L + n log n)

这样,在理论复杂度上就可以过全部数据了。

可怜我在BZOJ上跑得比暴力还慢。。。不过大数据确实可以在2s内过,而且卡不了。

5366d0160924ab18b54e6f3a37fae6cd7b890b66.jpg.png

代码:

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <cmath>

  

using namespace std;

  

#define lowbit(x)((-x)&x)

#define MAXN 20010

#define MAXM 50010

#define MAXL 500010

#define inf 0x7fffffff

#define MAXD 21

  

int s[MAXL],name[MAXL];

int n,m,lenn[MAXN],lenm[MAXM],firn[MAXN],firm[MAXM],N=0;

  

int getstring() {

    int len;

    scanf("%d",&len);

    for (int i=0;i++<len;) scanf("%d",&name[i]);

    return len;

}

  

int sa[MAXL],rank[MAXL],r[MAXL],w[MAXL],x[MAXL],y[MAXL],height[MAXL],b,Nn;

struct saver {

    int v,t;

} c[MAXL];

bool cmp(saver x,saver y) {

    return x.v<y.v;

}

  

void Make_sa() {

    for (int i=0;i++<N;) c[i].v=s[i],c[i].t=i;

    sort(c+1,c+N+1,cmp);

    Nn=0;

    for (int i=0;i++<N;) {

        if (i==1||c[i].v!=c[i-1].v) Nn++;

        rank[c[i].t]=Nn;

    }

    b=1;

    x[sa[0]=0]=y[0]=-1;

    do {

        for (int i=0;i++<N;) x[i]=rank[i],y[i]=i+b<=N?rank[i+b]:0;

        b<<=1;

        for (int i=0;i<=N;i++) w[i]=0;

        for (int i=0;i++<N;) w[y[i]]++;

        for (int i=0;i++<N;) w[i]+=w[i-1];

        for (int i=0;i++<N;) r[w[y[i]]--]=i;

        for (int i=0;i<=N;i++) w[i]=0;

        for (int i=0;i++<N;) w[x[r[i]]]++;

        for (int i=0;i++<N;) w[i]+=w[i-1];

        for (int i=N;i;i--) sa[w[x[r[i]]]--]=r[i];

        Nn=0;

        for (int i=0;i++<N;) {

            if (x[sa[i]]!=x[sa[i-1]]||y[sa[i]]!=y[sa[i-1]]) Nn++;

            rank[sa[i]]=Nn;

        }

    } while (Nn<N);

    int k=0;

    for (int i=0;i++<N;) {

        k=max(k-1,0);

        height[rank[i]]=k;

        for (int j=k;i+j<=N&&sa[rank[i]-1]+j<=N&&s[i+j]==s[sa[rank[i]-1]+j];j++) height[rank[i]]++;

        k=height[rank[i]];

    }

}

  

int st[MAXL][MAXD];

  

void Init_st() {

    for (int i=0;i++<N;) st[i][0]=height[i];

    int d=int(log2(N))+1;

    for (int i=0;i++<d;) {

        for (int j=0;j++<N;) {

            st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);

        }

    }

}

  

int Min(int l,int r) {

    int k=int(log2(r-l+1));

    return min(st[l][k],st[r-(1<<k)+1][k]);

}

  

int ansn[MAXN],ansm[MAXM],left[MAXM],right[MAXM];

  

struct node {

    node *next;

    int l,r,t;

    bool flag;

} *head[MAXL];

  

void Insert(int w,int l,int r,int t,bool flag) {

    node *p=new(node);

    p->next=head[w],p->l=l,p->r=r,p->t=t,p->flag=flag;

    head[w]=p;

}

  

int next[MAXL],last[MAXL];

  

bool Cmp(int x,int y) {

    return x<y;

}

  

struct Bit{

    int t[MAXL],n;

  

    void Init(int _n) {

        memset(t,0,sizeof(t));

        n=_n;

    }

  

    void Add(int x,int y) {

        for (;x<=n;x+=lowbit(x)) t[x]+=y;

    }

  

    int Sum(int x) {

        int rec=0;

        for (;x;x-=lowbit(x)) rec+=t[x];

        return rec;

    }

} bit;

  

void Solve0() {

    memset(head,0,sizeof(head));

    for (int i=0;i++<m;) Insert(left[i],left[i],right[i],i,true);

    memset(next,0,sizeof(next));

    memset(last,0,sizeof(last));

    memset(w,0,sizeof(w));

    bit.Init(N);

    for (int i=0;i++<n;) {

        for (int j=0;j<lenn[i];j++) r[j]=rank[firn[i]+j];

        sort(r,r+lenn[i],Cmp);

        bit.Add(r[0],1);

        for (int j=0;j<lenn[i]-1;j++) next[r[j]]=r[j+1];

        for (int j=1;j<lenn[i];j++) last[r[j]]=r[j-1];

        for (int j=0;j<lenn[i];j++) w[r[j]]=i;

    }

    for (int i=0;i++<N;) {

        for (node *p=head[i];p;p=p->next) ansm[p->t]=bit.Sum(p->r);

        if (w[i]) bit.Add(i,-1);

        if (next[i]) bit.Add(next[i],1);

    }

}

  

void Solve1() {

    memset(head,0,sizeof(head));

    memset(ansn,0,sizeof(ansn));

    for (int i=0;i++<m;) Insert(left[i],left[i],right[i],i,true),Insert(right[i]+1,left[i],right[i],i,false);

    bit.Init(N);

    for (int i=0;i<=N;i++) {

        for (node *p=head[i];p;p=p->next) if (p->flag) bit.Add(p->l,1); else bit.Add(p->l,-1);

        ansn[w[i]]+=bit.Sum(i)-bit.Sum(last[i]);

    }

}

  

void Solve() {

    Init_st();

    for (int i=0;i++<m;) {

        if (height[rank[firm[i]]]<lenm[i]) left[i]=rank[firm[i]]

            ;  else {

                int le=1,ri=rank[firm[i]];

                while (ri-le>1) {

                    int mid=(le+ri)>>1;

                    if (Min(mid,rank[firm[i]])>=lenm[i]) ri=mid

                        ;  else le=mid;

                }

                left[i]=le;

            }

        if (height[rank[firm[i]]+1]<lenm[i]) right[i]=rank[firm[i]]

            ;  else if (Min(rank[firm[i]]+1,N)>=lenm[i]) right[i]=N

                ;  else {

                    int le=rank[firm[i]]+1,ri=N;

                    while (ri-le>1) {

                        int mid=(le+ri)>>1;

                        if (Min(rank[firm[i]]+1,mid)>=lenm[i]) le=mid

                            ;  else ri=mid;

                    }

                    right[i]=le;

                }

    }

    Solve0();

    Solve1();

}

  

int main() {

    s[0]=inf-1;

    scanf("%d%d",&n,&m);

    for (int i=0;i++<n;) {

        lenn[i]=getstring(),firn[i]=N+1;

        for (int j=0;j++<lenn[i];) s[++N]=name[j];

        int delta=getstring();

        lenn[i]++,s[++N]=-inf;

        for (int j=0;j++<delta;) s[++N]=name[j];

        s[++N]=-inf;

        lenn[i]+=delta;

    }

    for (int i=0;i++<m;) {

        firm[i]=N+1,lenm[i]=getstring();

        for (int j=0;j++<lenm[i];) s[++N]=name[j];

        s[++N]=-inf;

    }

    Make_sa();

    Solve();

    for (int i=0;i++<m;) printf("%d\n",ansm[i]);

    for (int i=0;i++<n-1;) printf("%d ",ansn[i]);

    printf("%d\n",ansn[n]);

    return 0;

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 这个不错分享给大家,从扣上看到的,就转过来了 《电脑专业英语》 file [fail] n. 文件;v. 保存文...
    麦子先生R阅读 6,546评论 5 24
  • 算法思想贪心思想双指针排序快速选择堆排序桶排序荷兰国旗问题二分查找搜索BFSDFSBacktracking分治动态...
    第六象限阅读 3,051评论 0 0
  • 各校历年复试机试试题 清华、北大、华科试题详细笔记部分,少笔记部分与少数leetcode【含个人整理笔记】 一、详...
    十里江城阅读 1,177评论 0 1
  • 归去来兮。 1.1 说明 本篇为《挑战程序设计竞赛(第2版)》[http://www.ituring.com.cn...
    尤汐Yogy阅读 14,274评论 0 160
  • 划线过程中常见问题 线的宽度有时会比预设的宽,边缘颜色出现浅色线 画一个1个像素的线 Points(pt)与Pix...
    mtry阅读 1,318评论 0 1