P2336 [SCOI2012]喵星球上的点名

题意

我们将所有姓名串和点名串拼在一起,中间用原串中不会出现的数隔开,对每个点记录它是哪个串的。

先考虑第一问:

考虑如果点名串\(s1\)是某个姓名串\(s2\)的子串,则\(s1\)必定和\(s2\)的一个后缀\(s2'\)\(lcp\)\(s1\)的长度,即:\(lcp(s1,s2')=len(s1)\)

对于一个点名串,以它作为前缀的后缀在后缀排序后必定是一个区间,我们可以二分得出,设为\([l,r]\),我们接下来就要统计\([l,r]\)内出现的不同的姓名的个数,这是个经典问题,可以用\(HH\)的项链的的方法离线解决。

再考虑第二问:

我们可以算出每个点被多少询问区间覆盖,但这样会计重。

我们从\(1\)\(n\)\(n\)是串长)扫描所有位置,同时维护可以对这个位置产生贡献的区间。具体说就是对于\([l,r]\),我们在扫到\(l\)时它开始产生贡献,我们对\([l,r]\)区间\(+1\),在扫到\(r+1\)时,它不再产生贡献,我们对\([l,r]\)区间\(-1\),这个可以用差分实现。

现在考虑怎么算出当前位置\(i\)真正的贡献,即算出只包含\(i\),而不包含\(i\)所在的原串的其他点的区间。我们找到\(last_{pos_i}\),其中\(pos_i\)表示\(i\)所在原串,\(last_{pos_i}\)表示上一个属于\(pos_i\)这个原串的位置,那么答案就是\(sum_i-sum_{last_{pos_i}}\)

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=5*1e4+10;
const int maxQ=1e5+10;
const int maxl=1e6+10;
int n,m,Q,num;
int a[maxl],len[maxl],firpos[maxl],pos[maxl],last[maxm],ans1[maxQ],ans2[maxm];
int sa[maxl],rk[maxl],oldrk[maxl],id[maxl],tmpid[maxl],cnt[maxl],lg[maxl];
int height[maxl][20];
struct Query{int l,r,id;}qr[maxQ];
inline void reads(int now)
{
    firpos[now]=n+1;
    scanf("%d",&len[now]);
    for(int i=1;i<=len[now];i++)scanf("%d",&a[++n]),pos[n]=now;
    a[++n]=now+10000;
}
inline bool cmp(Query x,Query y){return x.r<y.r;}
inline bool check(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
inline void SA_build()
{
    num=500000;
    for(int i=1;i<=n;i++)cnt[rk[i]=a[i]]++;
    for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
    for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
    for(int t=1;t<=n;t<<=1)
    {
        int tot=0;
        for(int i=n-t+1;i<=n;i++)id[++tot]=i;
        for(int i=1;i<=n;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
        tot=0;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)cnt[tmpid[i]=rk[id[i]]]++;
        for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
        for(int i=n;i;i--)sa[cnt[tmpid[i]]--]=id[i];
        memcpy(oldrk,rk,sizeof(rk));
        for(int i=1;i<=n;i++)rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
        num=tot;
    }
    for(int i=1,j=0;i<=n;i++)
    {
        if(j)j--;
        while(a[i+j]==a[sa[rk[i]-1]+j])j++;
        height[rk[i]][0]=j;
    }
    for(int j=1;j<=18;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            height[i][j]=min(height[i][j-1],height[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r)
{
    l++;
    int t=lg[r-l+1];
    return min(height[l][t],height[r-(1<<t)+1][t]);
}
inline int findl(int id)
{
    int l=1,r=rk[firpos[id]]-1,res=rk[firpos[id]];
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(query(mid,rk[firpos[id]])<len[id])l=mid+1;
        else res=mid,r=mid-1;
    }
    return res;
}
inline int findr(int id)
{
    int l=rk[firpos[id]]+1,r=n,res=rk[firpos[id]];
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(query(rk[firpos[id]],mid)<len[id])r=mid-1;
        else res=mid,l=mid+1;
    }
    return res;
}
struct Tree_arry
{
    #define lowbit(x) (x&-x)
    int a[maxl];
    inline void clear(){memset(a,0,sizeof(a));}
    inline void add(int x,int k){for(int i=x;i<=n;i+=lowbit(i))a[i]+=k;}
    inline int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=a[i];return res;}
}tr;
int main()
{
    lg[0]=-1;
    for(int i=1;i<=1e6;i++)lg[i]=lg[i>>1]+1;
    scanf("%d%d",&m,&Q);
    for(int i=1;i<=m;i++)reads(2*i-1),reads(2*i);
    for(int i=1;i<=Q;i++)reads(2*m+i);
    SA_build();
    for(int i=1;i<=Q;i++)qr[i].l=findl(2*m+i),qr[i].r=findr(2*m+i),qr[i].id=i;
    sort(qr+1,qr+Q+1,cmp);
    for(int i=1,j=1;i<=Q;i++)
    {
        while(j<=qr[i].r)
        {
            if(pos[sa[j]]<=2*m)
            {
                if(last[(pos[sa[j]]+1)>>1])tr.add(last[(pos[sa[j]]+1)>>1],-1);
                last[(pos[sa[j]]+1)>>1]=j;
                tr.add(j,1);
            }
            j++;
        }
        ans1[qr[i].id]=tr.query(qr[i].r)-tr.query(qr[i].l-1);
    }
    for(int i=1;i<=m;i++)last[i]=0;
    tr.clear();
    for(int i=1;i<=Q;i++)tr.add(qr[i].l,1);
    for(int i=1,j=1;i<=n;i++)
    {
        while(j<=Q&&qr[j].r<i)tr.add(qr[j].l,-1),j++;
        if(pos[sa[i]]<=2*m)
        {
            ans2[(pos[sa[i]]+1)>>1]+=tr.query(i)-tr.query(last[(pos[sa[i]]+1)>>1]);
            last[(pos[sa[i]]+1)>>1]=i;  
        }
    } 
    for(int i=1;i<=Q;i++)printf("%d\n",ans1[i]);
    for(int i=1;i<=m;i++)printf("%d ",ans2[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/nofind/p/12084157.html