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;
}