bzoj2754 scoi2012 喵星球的点名

36 篇文章 0 订阅
9 篇文章 0 订阅

http://www.elijahqi.win/archives/615
题目描述

a180285 幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。

假设课堂上有 N 个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M 个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。

然而,由于喵星人的字码过于古怪,以至于不能用 ASCII 码来表示。为了方便描述,a180285 决定用数串来表示喵星人的名字。

现在你能帮助 a180285 统计每次点名的时候有多少喵星人答到,以及 M 次点名结束后每个喵星人答到多少次吗?

输入输出格式

输入格式:

现在定义喵星球上的字符串给定方法:

先给出一个正整数 L ,表示字符串的长度,接下来L个整数表示字符串的每个字符。

输入的第一行是两个整数 N 和 M。

接下来有 N 行, 每行包含第 i 个喵星人的姓和名两个串。 姓和名都是标准的喵星球上的字符串。

接下来有 M 行,每行包含一个喵星球上的字符串,表示老师点名的串。

输出格式:

对于每个老师点名的串输出有多少个喵星人应该答到。

然后在最后一行输出每个喵星人被点到多少次。

输入输出样例

输入样例#1:

2 3
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25
输出样例#1:

2
1
0
1 2
说明

事实上样例给出的数据如果翻译成地球上的语言可以这样来看

2 3 izayoi sakuya

orihara izaya

izay hara raiz

对于 30%的数据,保证:

1<=N,M<=1000,喵星人的名字总长不超过 4000,点名串的总长不超过 2000,

对于 100%的数据,保证:

1<=N<=20000 ,1<=M<=50000. 喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过 10000

有了之前的经历,二分的速度似乎还不如暴力搞搞速度快呢

这里相比上一个tjoi2013需要限制一下不要查到问询的字符串里去 并且假如是属于同一块那么tmp记录的答案只统计一次


#include<cstdio>
#include<cstring>
#define N 2020010
inline int read(){
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch<='9'&&ch>='0') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,m,a[N],n1,m1,bl[N],count[N],rank[N<<1],rank1[N],sa[N],height[N],k,tmp[N],visit[N],ans[N];
struct node{
    int st,len;
}data[55000];
int main(){
//    freopen("2336.in","r",stdin);
    n1=read();m1=read();n=1;m=10000;
    for (int i=1;i<=n1;++i){
        int tmp=read();
        for (int j=0;j<tmp;++j) a[n+j]=read(),bl[n+j]=i;n+=tmp;a[n++]=++m;
        tmp=read();
        for (int j=0;j<tmp;++j) a[n+j]=read(),bl[n+j]=i;n+=tmp;a[n++]=++m;
    }int last=n-1;
    for (int i=1;i<=m1;++i){
        int tmp=read();
        data[i].st=n;data[i].len=tmp;
        for (int j=0;j<tmp;++j) a[n+j]=read();n+=tmp;a[n++]=++m;
    }n-=1;
//    printf("%d\n",n);
    //for (int i=1;i<=n;++i) printf("%d\n",a[i]);
    for (int i=1;i<=n;++i) count[a[i]]=1;
    for (int i=1;i<=m;++i) count[i]+=count[i-1];
    for (int i=1;i<=n;++i) rank[i]=count[a[i]];
    k=0;count[0]=0;
    for (int p=1;k!=n;p<<=1,m=k){
        for (int i=1;i<=m;++i) count[i]=0;
        for (int i=1;i<=n;++i) count[rank[i+p]]++;
        for (int i=1;i<=m;++i) count[i]+=count[i-1];
        for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
        for (int i=1;i<=m;++i) count[i]=0;
        for (int i=1;i<=n;++i) count[rank[i]]++;
        for (int i=1;i<=m;++i) count[i]+=count[i-1];
        for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
        memcpy(rank1,rank,sizeof(rank)>>1);
        rank[sa[1]]=k=1;
        for (int i=2;i<=n;++i){
            if (rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
            rank[sa[i]]=k;
        }
    }
    k=0;
    for (int i=1;i<=n;++i){
        if (rank[i]==1) continue;
        k=k==0?0:k-1;
        while (a[i+k]==a[sa[rank[i]-1]+k]) ++k;
        height[rank[i]]=k;
    }
//    for (int i=1;i<=n;++i) printf("%d ",sa[i]);printf("\n");
//    for (int i=1;i<=n;++i) printf("%d ",height[i]);
    for (int i=1;i<=m1;++i){
        int l=rank[data[i].st],r=rank[data[i].st]+1;
        while (height[l]>=data[i].len) --l;
        while (height[r]>=data[i].len) ++r;--r;
        int tmp=0;
        for (int j=l;j<=r;++j){
            if (sa[j]>last) continue;//超过了答案寻找的范围
            if (visit[bl[sa[j]]]==i) continue; 
            tmp++;ans[bl[sa[j]]]++;visit[bl[sa[j]]]=i;
        }
        printf("%d\n",tmp);
    }
    for (int i=1;i<=n1;++i) printf("%d ",ans[i]);
    return 0;
}

二分查找x

#include<cstdio>
#include<cstring>
#define N 2020010
inline int read(){
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch<='9'&&ch>='0') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,m,a[N],n1,m1,bl[N],count[N],rank[N<<1],rank1[N],sa[N],height[N],k,tmp[N],visit[N],ans[N],l,r,fmin[N][20],Log[N];
struct node{
    int st,len;
}data[55000];
inline int min(int x,int y){return x<y?x:y;}
inline int lcp(int x,int y){
    x++;int t=Log[y-x+1];
    return min(fmin[x][t],fmin[y-(1<<t)+1][t]);
}
void check(int st,int len){
    int l1,r1;
    if (height[st]<len) l1=st;else{
        int ll=1,rr=st-1;
        while (ll<=rr){
            int mid=(ll+rr)>>1;
            if(lcp(mid,st)>=len) rr=mid-1;else ll=mid+1;
        }
        l1=ll;
    }
    if (height[st+1]<len) r1=st;else{
        int ll=st+1,rr=n;
        while (ll<=rr){
            int mid=(ll+rr)>>1;
            if (lcp(st,mid)>=len) ll=mid+1;else rr=mid-1;
        }
        r1=rr;
    }
    l=l1;r=r1;
}
int main(){
    freopen("2336.in","r",stdin);
    n1=read();m1=read();n=1;m=10000;
    for (int i=1;i<=n1;++i){
        int tmp=read();
        for (int j=0;j<tmp;++j) a[n+j]=read(),bl[n+j]=i;n+=tmp;a[n++]=++m;
        tmp=read();
        for (int j=0;j<tmp;++j) a[n+j]=read(),bl[n+j]=i;n+=tmp;a[n++]=++m;
    }int last=n-1;
    for (int i=1;i<=m1;++i){
        int tmp=read();
        data[i].st=n;data[i].len=tmp;
        for (int j=0;j<tmp;++j) a[n+j]=read();n+=tmp;a[n++]=++m;
    }n-=1;
//  printf("%d\n",n);
    //for (int i=1;i<=n;++i) printf("%d\n",a[i]);
    for (int i=1;i<=n;++i) count[a[i]]=1;
    for (int i=1;i<=m;++i) count[i]+=count[i-1];
    for (int i=1;i<=n;++i) rank[i]=count[a[i]];
    k=0;count[0]=0;
    for (int p=1;k!=n;p<<=1,m=k){
        for (int i=1;i<=m;++i) count[i]=0;
        for (int i=1;i<=n;++i) count[rank[i+p]]++;
        for (int i=1;i<=m;++i) count[i]+=count[i-1];
        for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
        for (int i=1;i<=m;++i) count[i]=0;
        for (int i=1;i<=n;++i) count[rank[i]]++;
        for (int i=1;i<=m;++i) count[i]+=count[i-1];
        for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
        memcpy(rank1,rank,sizeof(rank)>>1);
        rank[sa[1]]=k=1;
        for (int i=2;i<=n;++i){
            if (rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
            rank[sa[i]]=k;
        }
    }
    k=0;
    Log[0]=-1;
    for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    for (int i=1;i<=n;++i){
        if (rank[i]==1) continue;
        k=k==0?0:k-1;
        while (a[i+k]==a[sa[rank[i]-1]+k]) ++k;
        height[rank[i]]=k;
    }
    for (int i=1;i<=n;++i) fmin[i][0]=height[i];
    for (int j=1;j<=Log[n];++j){
        for (int i=1;i<=n-(1<<j)+1;++i){
            fmin[i][j]=min(fmin[i][j-1],fmin[i+(1<<(j-1))][j-1]);
        }
    }
//  for (int i=1;i<=n;++i) printf("%d ",sa[i]);printf("\n");
//  for (int i=1;i<=n;++i) printf("%d ",height[i]);
    for (int i=1;i<=m1;++i){
        check(rank[data[i].st],data[i].len);
        int tmp=0;
        for (int j=l;j<=r;++j){
            if (sa[j]>last) continue;//超过了答案寻找的范围
            if (visit[bl[sa[j]]]==i) continue; 
            tmp++;ans[bl[sa[j]]]++;visit[bl[sa[j]]]=i;
        }
        printf("%d\n",tmp);
    }
    for (int i=1;i<=n1;++i) printf("%d ",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值