• [SCOI2012]喵星球上的点名


    X.[SCOI2012]喵星球上的点名

    我居然做出了这题……难以置信!

    首先,思路很明显是把所有串全怼一起(包括名字和询问串),加上分隔符,然后跑一遍后缀数组。

    我们仍然可以用单调栈求出关于每个询问串与它相同的区间。即,如果以询问串为前缀的那个后缀的\(rank\)\(p\)的话,它的合法区间\([L,R]\)应该满足\(\forall L<i\leq R,ht_i\geq ht_p\)。注意这里是\(\geq\)而非\(>\),因此应该特别注意处理\(=\)的部分。

    这里放一下求出全部\([L,R)\)的代码(注意这里数组中的\([L,R)\)实际上是左闭右开的,而我们最终要求的\([L,R]\)是左闭右闭的,所以接下来用的时候\(R\)要减去\(1\)):

    for(int i=1;i<n;i++){
    	while(tp&&ht[stk[tp]]>ht[i])R[stk[tp--]]=i;
    	if(ht[stk[tp]]==ht[i])L[i]=L[stk[tp]];
    	else L[i]=stk[tp];
    	stk[++tp]=i;
    }
    while(tp)R[stk[tp--]]=n;
    

    求出所有合法的\([L,R]\)后,我们可以考虑那两个询问了。

    首先,对于第一问,发现它就是求区间\([L,R]\)内部所有出现过的姓名串的数量。这不就是[SDOI2009]HH的项链吗?树状数组即可。当然你要真想写莫队也没人拦得住你

    然后,关于第二问,它的意义实际上是将区间\([L,R]\)内部所有出现过的姓名串的计数器增加\(1\)。如果关于询问串下手我们没有办法,但是我们可以关于姓名串下手呀!

    我们设一个下标\(i\),它所代表的姓名串为代码中的\(id\big[sa[i]\big]\),我们这里设一个\(c_i\)代表它。我们再设它之前上一个出现的该姓名串的下标为\(LAS_i\)(如果之前没有出现任何这个姓名串的下标,\(LAS_i=-1\))。则显然,所有满足\(i\in[L,R]\)\(LAS_i<L\)的区间\([L,R]\)都可以对\(c_i\)有贡献。这是什么?二维数点问题呀!

    于是我们愉快地排个序就能用线段树求出答案。

    复杂度\(O(n\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int S,T,cnt,id[400100],tms[400100],ans[400100],stt[400100],P,Q,len[400100];
    //-------------------Suffix Array Below--------------------
    int stk[400100],tp,L[400100],R[400100];
    namespace Suffix_Array{
    	int x[400100],y[400100],sa[400100],ht[400100],rk[400100],buc[400100],s[400100],n,m;
    	bool mat(int a,int b,int k){
    		if(y[a]!=y[b])return false;
    		if((a+k<n)^(b+k<n))return false;
    		if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
    		return true;
    	}
    	void SA(){
    		for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
    		for(int k=1;k<n;k<<=1){
    			int num=0;
    			for(int i=n-k;i<n;i++)y[num++]=i;
    			for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
    			for(int i=0;i<=m;i++)buc[i]=0;
    			for(int i=0;i<n;i++)buc[x[y[i]]]++;
    			for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    			for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i],y[i]=0;
    			swap(x,y);
    			x[sa[0]]=num=0;
    			for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    			m=num;
    		}
    		for(int i=0;i<n;i++)rk[sa[i]]=i;
    		for(int i=0,k=0;i<n;i++){
    			if(!rk[i])continue;
    			if(k)k--;
    			int j=sa[rk[i]-1];
    			while(j+k<n&&i+k<n&&s[j+k]==s[i+k])k++;
    			ht[rk[i]]=k;
    		}
    	}	
    }
    using namespace Suffix_Array;
    //------------------Suffix Array Above---------------------
    int las[400100],LAS[400100];
    //------------------Ask the Queries Below------------------
    struct Query{
    	int l,r,id;
    	Query(int a=0,int b=0,int c=0){l=a,r=b,id=c;}
    }q[400100];
    bool cmp1(Query &u,Query &v){
    	return u.r<v.r;
    }
    int t[400100];
    void add(int x,int y){
    	x++;
    	while(x<=n)t[x]+=y,x+=x&-x;
    }
    int sum(int x){
    	x++;
    	int ret=0;
    	while(x)ret+=t[x],x-=x&-x;
    	return ret;
    }
    //------------------Ask the Queries Above------------------
    //-----------------Calculate the Times Below---------------
    pair<int,int>p[400100];
    bool cmp(pair<int,int>&u,pair<int,int>&v){//first:place second:last
    	return u.second>v.second;
    }
    bool cmp2(Query &u,Query &v){
    	return u.l>v.l;
    }
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    int tag[1600100];
    void pushdown(int x){tag[lson]+=tag[x],tag[rson]+=tag[x],tag[x]=0;}
    void modify(int x,int l,int r,int L,int R){
    	if(l>R||r<L)return;
    	if(L<=l&&r<=R){tag[x]++;return;}
    	pushdown(x),modify(lson,l,mid,L,R),modify(rson,mid+1,r,L,R);
    }
    int query(int x,int l,int r,int P){
    	if(l>P||r<P)return 0;
    	if(l==r)return tag[x];
    	pushdown(x);
    	return query(lson,l,mid,P)+query(rson,mid+1,r,P);
    }
    #undef lson
    #undef rson
    #undef mid
    //----------------------Calculate the Times Above----------
    int main(){
    	scanf("%d%d",&S,&T),cnt=10000;
    	for(int i=1;i<=S;i++){
    		scanf("%d",&m);
    		for(int j=0;j<m;j++)scanf("%d",&s[n+j]),id[n+j]=i;
    		n+=m;
    		s[n++]=++cnt;
    		scanf("%d",&m);
    		for(int j=0;j<m;j++)scanf("%d",&s[n+j]),id[n+j]=i;
    		n+=m;
    		s[n++]=++cnt;
    	}
    	for(int i=1;i<=T;i++){
    		scanf("%d",&m),stt[i]=n;
    		len[i]=m;
    		for(int j=0;j<m;j++)scanf("%d",&s[n+j]),id[n+j]=S+i;
    		n+=m;
    		s[n++]=++cnt;
    	}
    	m=cnt;
    	SA();
    	for(int i=1;i<n;i++){
    		while(tp&&ht[stk[tp]]>ht[i])R[stk[tp--]]=i;
    		if(ht[stk[tp]]==ht[i])L[i]=L[stk[tp]];
    		else L[i]=stk[tp];
    		stk[++tp]=i;
    	}
    	while(tp)R[stk[tp--]]=n;
    	for(int i=1;i<=T;i++)if(ht[rk[stt[i]]]==len[i])q[++Q]=Query(L[rk[stt[i]]],R[rk[stt[i]]]-1,i);
    //	for(int i=0;i<n;i++)printf("%2d::S:%5d id:%d rk:%2d sa:%2d ht:%d\n",i,s[i],id[i],rk[i],sa[i],ht[i]);
    //	for(int i=1;i<=T;i++)printf("%d %d\n",q[i].l,q[i].r);
    	sort(q+1,q+Q+1,cmp1),memset(las,-1,sizeof(las));
    	for(int i=1,j=0;i<=Q;i++){
    		for(;j<=q[i].r;j++){
    			if(id[sa[j]]>S||!id[sa[j]])continue;
    			LAS[j]=las[id[sa[j]]],las[id[sa[j]]]=j;
    			if(LAS[j]!=-1)add(LAS[j],-1);
    			add(j,1);
    			p[++P]=make_pair(j,LAS[j]);
    		}
    		ans[q[i].id]=sum(q[i].r)-sum(q[i].l-1);
    	}
    	for(int i=1;i<=T;i++)printf("%d\n",ans[i]);
    	sort(q+1,q+Q+1,cmp2),sort(p+1,p+P+1,cmp);
    	for(int i=1,j=1;i<=P;i++){
    		while(j<=Q&&p[i].second<q[j].l)modify(1,0,n-1,q[j].l,q[j].r),j++;
    		tms[id[sa[p[i].first]]]+=query(1,0,n-1,p[i].first);
    	}
    	for(int i=1;i<=S;i++)printf("%d ",tms[i]);
    	return 0;
    }
    

  • 相关阅读:
    Java Swing3-MyDialog的基本实现
    Java IO4 实现凯撒密码加密解密文本文件
    POJ 3869 条件概率
    2017年校招全国统一模拟笔试(第三场)编程题集合
    POJ 2800 Joseph’s Problem 数论找规律
    数据结构期末考试再复习
    Java Swing2 基本登录框
    Java Swing1 基本框架
    Java 多线程1 烧水开水喝茶案例
    Java IO3 把一个图片写进txt,再写出还原图片
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605160.html
Copyright © 2020-2023  润新知