Description

给定 \(N\) 个姓名串和 \(M\) 个点名串。询问每个点名串点到了多少姓名和每个姓名串被点到了几次。\(N\leq 5\cdot 10^4,M\leq 10^5\)。

Sol

卡了我一周90分的题原来是数组开小我就艹了我就

最开始以为是 \(AC\) 自动机裸题但是细想了一下发现并不会,还是老老实实想 \(SA\) 。

首先要把这些串连在一起,然后跑一遍 \(SA\) ,可以先求出每个点名串到底点到了多少姓名串(也就是说在 \(SA\) 上有多少位置和点名串的 \(LCP\) 是点名串自己),发现这个在 \(SA\) 上是一段区间,可以二分左右端点求出。时间复杂度 \(O(n\log n)\)。

然后这两问分别考虑,第一问就相当于区间数颜色,可以离线+树状数组解决。就是按照区间右端点排序,如果当前的值 \(a[i]=x\),那就在 \(i\) 处 \(+1\),在 \(x\) 上一次出现的位置 \(las[x]\) 处 \(-1\)。然后统计区间 \([L,R]\) 直接树状数组上查就好了。这个算法的核心思路是保证相同数字的贡献只有 \(1\),也就是说每个颜色只会在前缀和数组中出现 \(1\) 次。这部分时间复杂度 \(O(n\log n)\)。

第二问就是求每个颜色被几个不同的区间覆盖了。这个同样离线,对于一个区间 \([L,R]\),我们在扫到 \(L\) 位置的时候在 \(L\) 处 \(+1\),扫到 \(R+1\) 的时候撤销 \(L\) 的 \(+1\) 操作。做到 \(i\) 位置的时候,设 \(a[i]=x\),记录 \(x\) 上一次出现的位置 \(las[x]\),那 \(ans[x]\) 就要加上 \((las[x],i]\) 的和。正确性就是如果一个区间覆盖了某个数,肯定会在左端点之后第一个出现这个数的位置统计上答案。这里的时间复杂度也是 \(O(n\log n)\)。

Code

这道题写的是真的丑...

把压行的都改掉了发现还是丑...

#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
const int N=1e6+5;
const int M=1e4+5;
// wtf?????
// 把m开大就过了???????
// 卡了老子一周的狗题????
int ans[N],ans2[N],st[N][20],fs[N],len[N];
int s[N],tot,sa[N],rk[N],height[N],lg[N],f[N];
int n,m,num,x[N],y[N],c[N],belong[N][2],las[N];//belong=2 refers to question struct Ques{
int l,r,idx;
friend bool operator<(Ques a,Ques b){
return a.r<b.r;
}
}ques[N]; struct qu{
int type,x;
qu(){}
qu(int a,int b){type=a,x=b;}
}; std::vector<qu> v[N]; void add(int *f,int x,int y){
while(x<=tot)
f[x]+=y,x+=x&-x;
} int query(int *f,int x){
int now=0;
while(x) now+=f[x],x-=x&-x;
return now;
} void getsa(int m=100001){
for(int i=1;i<=tot;i++) c[x[i]=s[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=tot;i;i--) sa[c[x[i]]--]=i;
for(int k=1;num=0,k<=tot;k<<=1){
for(int i=tot-k+1;i<=tot;i++) y[++num]=i;
for(int i=1;i<=tot;i++) if(sa[i]>k) y[++num]=sa[i]-k;
for(int i=0;i<=m;i++) c[i]=0;
for(int i=1;i<=tot;i++) c[x[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=tot;i;i--) sa[c[x[y[i]]]--]=y[i];
swap(x,y),x[sa[1]]=num=1;
for(int i=2;i<=tot;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]] and y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
if(num==tot) return;m=num;
}
} void getheight(int k=0){
for(int i=1;i<=tot;i++)
rk[sa[i]]=i;
for(int i=1;i<=tot;i++){
if(rk[i]==1) continue;
if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=tot and j+k<=tot and s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
} void getst(){
for(int i=2;i<=tot;i++)
lg[i]=lg[i-1]+((1<<lg[i-1]+1)==i);
for(int i=1;i<=tot;i++)
st[i][0]=height[i];
for(int k=1;k<=lg[tot];k++)
for(int i=1;i+(1<<k)-1<=tot;i++)
st[i][k]=min(st[i][k-1],st[i+(1<<k-1)][k-1]);
} int getint(){
int X=0,w=0;char ch=getchar();
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
} int query(int x,int y){
if(x==y) return tot-sa[x]+1;
if(x>y) swap(x,y);x++;
int k=lg[y-x+1];
return min(st[x][k],st[y-(1<<k)+1][k]);
} signed main(){
n=getint(),m=getint();int cnt=10001;
for(int i=1;i<=n;i++){
if(i!=1)
s[++tot]=++cnt;
int lenn=getint();
for(int j=1;j<=lenn;j++)
s[++tot]=getint()+1,belong[tot][0]=1,belong[tot][1]=i;
s[++tot]=++cnt;
lenn=getint();
for(int j=1;j<=lenn;j++)
s[++tot]=getint()+1,belong[tot][0]=1,belong[tot][1]=i;
}
for(int i=1;i<=m;i++){
s[++tot]=++cnt;
len[i]=getint();fs[i]=tot+1;
for(int j=1;j<=len[i];j++)
s[++tot]=getint()+1,belong[tot][0]=2,belong[tot][1]=i;
}
getsa(),getheight(),getst();
for(int i=1;i<=m;i++){
//rk[fs[i]]
int l=1,r=rk[fs[i]],now=0;
while(l<=r){
int mid=l+r>>1;
if(query(mid,rk[fs[i]])>=len[i]) now=mid,r=mid-1;
else l=mid+1;
}
ques[i].l=now;l=rk[fs[i]],r=tot,now=0;
while(l<=r){
int mid=l+r>>1;
if(query(rk[fs[i]],mid)>=len[i]) now=mid,l=mid+1;
else r=mid-1;
}
ques[i].r=now;ques[i].idx=i;
}
std::sort(ques+1,ques+1+m);int ptr=1;
for(int i=1;i<=tot;i++){
if(belong[sa[i]][0]==1 and las[belong[sa[i]][1]])
add(f,las[belong[sa[i]][1]],-1);
if(belong[sa[i]][0]==1)
add(f,i,1),las[belong[sa[i]][1]]=i;
while(ptr<=m and ques[ptr].r==i){
ans[ques[ptr].idx]=query(f,ques[ptr].r)-query(f,ques[ptr].l-1);
ptr++;
}
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
memset(f,0,sizeof f);memset(las,0,sizeof las);
for(int i=1;i<=m;i++)
v[ques[i].l].pb(qu(1,ques[i].l)),v[ques[i].r].pb(qu(-1,ques[i].l));
for(int i=1;i<=tot;i++){
for(auto x:v[i])
if(x.type==1)
add(f,x.x,x.type);
if(belong[sa[i]][0]==1)
ans2[belong[sa[i]][1]]+=query(f,i)-query(f,las[belong[sa[i]][1]]);
if(belong[sa[i]][0]==1)
las[belong[sa[i]][1]]=i;
for(auto x:v[i])
if(x.type==-1)
add(f,x.x,x.type);
} for(int i=1;i<=n;i++)
printf("%d%c",ans2[i],i==n?'\n':' ');
return 0;
}

[SCOI2012] 喵星球上的点名的更多相关文章

  1. BZOJ 2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 649  Solved: 305[Submit][Sta ...

  2. BZOJ2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 680  Solved: 314[Submit][Sta ...

  3. BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1906  Solved: 839[Submit][St ...

  4. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

  5. P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

    P2336 [SCOI2012]喵星球上的点名 名字怎么存?显然是后缀自动机辣 询问点到多少个喵喵喵其实就是 查询后缀自动机上parent树的一个子树 于是我们考虑莫队 怎么树上莫队呢 我们用dfs序 ...

  6. 洛咕 P2336 [SCOI2012]喵星球上的点名

    洛咕 P2336 [SCOI2012]喵星球上的点名 先求出SA和height,一个点名串对应的就是一段区间,还有很多个点,就转化成了 有很多个区间,很多个点集,对每个区间计算和多少个点集有交,对每个 ...

  7. 洛谷 P2336 [SCOI2012]喵星球上的点名 解题报告

    P2336 [SCOI2012]喵星球上的点名 题目描述 a180285 幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有 \(N\) 个喵星人,每个喵星人的 ...

  8. 【BZOJ2754】[SCOI2012]喵星球上的点名

    [BZOJ2754][SCOI2012]喵星球上的点名 题面 bzoj 洛谷 题解 这题有各种神仙做法啊,什么暴力\(AC\)自动机.\(SAM\)等等五花八门 我这个蒟蒻在这里提供一种复杂度正确且常 ...

  9. 2754. [SCOI2012]喵星球上的点名【后缀数组】

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

  10. SCOI2012喵星球上的点名

    http://codevs.cn/problem/2403/ 2012年省队选拔赛四川  时间限制: 2 s  空间限制: 128000 KB   题目描述 Description a180285幸运 ...

随机推荐

  1. activeMq-2 高可用以及集群搭建

    Activemq 的集群方法可以有多种实现方式,我们这里使用zookeeper来实现 要搭建集群,请确保已经搭建好zookeeper环境.这里不再演示. 基本原理: 使用ZooKeeper(集群)注册 ...

  2. 可以用到的XSS跨站语句

    我们常用的测试XSS跨站的语句一般是alert比如: <script>alert(“sex”)</script> <script>alert(/sex/)</ ...

  3. UVA 10100 Longest Match

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=13&p ...

  4. Maven学习 二 Maven环境搭建

    第一步:下载Maven并解压 注意选择镜像地址,选择国内的会快点 解压目录 Maven目录分析 bin:含有mvn运行的脚本 boot:含有plexus-classworlds类加载器框架 conf: ...

  5. 339. Nested List Weight Sum

    https://leetcode.com/problems/nested-list-weight-sum/description/ Given a nested list of integers, r ...

  6. ABP框架系列之十九:(Debugging-调试)

    While it's not generally needed, you may want to step into ABP's source code while you debugging you ...

  7. 8. American Friendship 美国式的友谊

    8. American Friendship 美国式的友谊 (1) Americans usually consider themselves a friendly people.Their frie ...

  8. Linux下设置快捷键

    以设置终端为例,进入Settings>>Keyboard>>Custom Shortcuts,点左下脚的+号,Name栏填入Treminal,command栏填入gnome-t ...

  9. Rabbit RPC 代码阅读(一)

    前言 因为想对RPC内部的机制作一个了解,特作以下阅读代码日志,以备忘. RPC介绍 Rabbit RPC 原理可以用3点概括: 1.服务端启动并且向注册中心发送服务信息,注册中心收到后会定时监控服务 ...

  10. 调试Release发布版程序的Crash错误(转)

    http://blog.sina.com.cn/s/blog_48f93b530100fsln.html 在Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问 ...