P2336 [SCOI2012] 喵星球上的点名 解题报告

oj:https://gxyzoj.com/d/gxyznoi/p/P107

SA+莫队

调了一天,真的心态炸了,总的来说这道题没有一步是好想的

首先,看到是多个字符串求一个是另一个子串,显然想到,讲这些字符串拼接起来,因为姓和名不能连在一起,所以可以在他们中间加一个没有出现的数字

接下来,首先考虑第一个问题

在拼接完后的子串上求出sa和height数组,那些地方能与模式串匹配成功?

可以考虑从模式串原位向两边扩展,此时,假设记当前串为x,若x与模式串的公共前缀大于等于模式串长度,则满足条件

因为两串之间的height是这一段的min,所以越往左或右,值就越小,具有单调性

所以,可以二分求出位置,记为l,r

接下来考虑如何求出这一个区间内的不同串的个数?

可以将每一个子串染成一种颜色,此时就是求一段区间内不同的颜色数量,显然莫队

接下来考虑第二个问题

可以使用一个差分的思想,在莫队求解的时候,如果这个颜色是第一次出现,当前操作为x,则加上m-x+1,就是假设·从这个位置开始的所有区间都会被覆盖

因为是假设,所以在它的个数降为0时,这一段区间的统计就结束了,假设当前的操作为y,就要减去多算的m-y+1

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf=1e4;
int N,M,n,m,a[400005],id[400005],cnt[800005],x[800005],y[800005];
int sa[800005],rk[400005],height[400005],mn[400005][21],ans1[100005];
int pos[100005],lt[100005],blen,bl[100005],ans[100005],sum;
struct node{
	int l,r,id;
}q[100005];
void st_min()
{
	for(int i=1;i<=n;i++)
	{
//		printf("%d ",height[i]);
		mn[i][0]=height[i];
	}
//	printf("\n");
	for(int j=1;(1<<j)<=n;j++)
	{
		for(int i=1;i<=n;i++)
		{
			mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
		}
	}
}
int query(int l,int r)
{
	int res=0;
	while((1<<(res+1))<=r-l+1) res++;
	return min(mn[l][res],mn[r-(1<<res)+1][res]);
}
int find1(int x,int v)
{
//	printf("%d\n",x);
	int l=1,r=x,ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
	//	printf("%d %d\n",mid,x);
		if(query(mid,x)>=v)
		{
			r=mid-1;
		}
		else
		{
			ans=mid;
			l=mid+1;
		}
	}
	if(ans==0) return x;
	return ans;
}
int find2(int x,int v)
{
//	printf("%d\n",x);
	int l=x+1,r=n,ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
//		printf("%d %d\n",x+1,mid);
		if(query(x+1,mid)>=v)
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	if(ans==0) return x;
	return ans;
}
bool cmp(node x,node y)
{
	if(bl[x.l]!=bl[y.l]) return x.l<y.l;
	return x.r<y.r;
}
void del(int x,int v)
{
	cnt[id[sa[x]]]--;
	if(cnt[id[sa[x]]]==0&&id[sa[x]])
	{
		sum--;
		ans1[id[sa[x]]]-=M-v+1;
	}
}
void add(int x,int v)
{
	cnt[id[sa[x]]]++;
	if(cnt[id[sa[x]]]==1&&id[sa[x]])
	{
		sum++;
		ans1[id[sa[x]]]+=M-v+1;
	}
}
int main()
{
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++)
	{
		int len,x;
		scanf("%d",&len);
		for(int j=1;j<=len;j++)
		{
			scanf("%d",&x);
			a[++n]=x;
			id[n]=i;
		}
		a[++n]=inf+1;
		scanf("%d",&len);
		for(int j=1;j<=len;j++)
		{
			scanf("%d",&x);
			a[++n]=x;
			id[n]=i;
		}
		a[++n]=inf+2;
	}
	for(int i=1;i<=M;i++)
	{
		scanf("%d",&lt[i]);
		pos[i]=n+1;
		for(int j=1;j<=lt[i];j++)
		{
			int x;
			scanf("%d",&x);
			a[++n]=x;
		}
		if(i!=M)
		a[++n]=inf+2;
	}
	m=8e5;
	for(int i=1;i<=n;i++) cnt[x[i]=a[i]]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>0;i--) sa[cnt[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1)
	{
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++) y[i]=sa[i];
		for(int i=1;i<=n;i++) cnt[x[y[i]+k]]++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>0;i--) sa[cnt[x[y[i]+k]]--]=y[i];
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++) y[i]=sa[i];
		for(int i=1;i<=n;i++) cnt[x[y[i]]]++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>0;i--) sa[cnt[x[y[i]]]--]=y[i];
		for(int i=1;i<=n;i++) y[i]=x[i];
		m=0;
		for(int i=1;i<=n;i++)
		{
			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])
			{
				x[sa[i]]=m;
			}
			else x[sa[i]]=++m;
		}
		if(m==n) break;
	}
	for(int i=1;i<=n;i++) rk[sa[i]]=i;
    for(int i=1,k=0;i<=n;i++)
	{
		if(rk[i]==1) continue;
		if(k) k--;
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k]) k++;
		height[rk[i]]=k;
	}
	st_min();
	for(int i=1;i<=M;i++)
	{
		int l=find1(rk[pos[i]],lt[i]);
		int r=find2(rk[pos[i]],lt[i]);
		q[i]=(node){l,r,i};
	//	printf("%d %d\n\n",l,r);
	}
	blen=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		bl[i]=(n-1)/blen+1;
	}
	sort(q+1,q+M+1,cmp);
	int l=1,r=0;
	memset(cnt,0,sizeof(cnt));
	for(int i=1;i<=M;i++)
	{
		while(l<q[i].l) del(l++,i);
		while(r>q[i].r) del(r--,i);
		while(l>q[i].l) add(--l,i);
		while(r<q[i].r) add(++r,i);
		ans[q[i].id]=sum;
	}
	for(int i=1;i<=M;i++)
	{
		printf("%d\n",ans[i]);
	}
	for(int i=1;i<=N;i++)
	{
		printf("%d ",ans1[i]);
	}
	return 0;
}
posted @ 2024-06-16 22:00  wangsiqi2010916  阅读(5)  评论(0编辑  收藏  举报