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;
}
12-24 07:54