bzoj-3207 花神的嘲讽计划Ⅰ

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个长度为n的序列,有m个询问;

每次给出一个区间[l,r]和一个长度为K的短序列;

查询区间中是否存在这个子串;

1<=n<=100000,1<=m<=100000,1<=K<=20;
题中所有数据不超过2*10^9;保证方案序列的每个数字<=n

题解:

这题我读了好几遍没读懂,看了题解才知道这问的是查询一个固定长度的字符串是否在区间出现

然后就是简单题了,用Hash来搞;

处理原串中所有的Hash值,然后如果[l,r]存在一个Hash值为A的子串,那么预处理的那一段中一定也有一个与其相同的元素;

利用离散化之后的可持久化线段树实现;

查询就是直接去查对应区间那个值是否存在;

时间复杂度O(nlogn),空间复杂度O(nlogn);

1A一道数据结构好愉悦啊!


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define M 5000000
#define seed 1000000007ll
using namespace std;
typedef unsigned long long ll;
int sum[M],ls[M],rs[M],tot;
int a[N],root[N],size;
ll hash[N],dis[N];
void Pushup(int no)
{
	sum[no]=sum[ls[no]]+sum[rs[no]];
}
void Insert(int l,int r,int &no,int val)
{
	int p=++tot;
	ls[p]=ls[no],rs[p]=rs[no];
	sum[p]=sum[no];
	no=p;
	if(l==r)
		sum[no]++;
	else
	{
		int mid=l+r>>1;
		if(val<=mid)Insert(l,mid,ls[no],val);
		else		Insert(mid+1,r,rs[no],val);
		Pushup(no);	
	}
}
int query(int l,int r,int nol,int nor,int val)
{
	if(l==r)
	{
		return sum[nor]-sum[nol];
	}
	else
	{
		int mid=l+r>>1;
		if(val<=mid)return query(l,mid,ls[nol],ls[nor],val);
		else		return query(mid+1,r,rs[nol],rs[nor],val);	
	}
}
int main()
{
	int n,m,len,i,j,k,x,y,l,r;
	ll powk,now;
	scanf("%d%d%d",&n,&m,&len);
	for(i=1;i<=n;i++)
	{
		scanf("%d",a+i);
		hash[i]=hash[i-1]*seed+a[i];
	}
	for(i=1,powk=1;i<=len;i++)
		powk*=seed;
	for(i=len;i<=n;i++)
	{
		dis[i-len+1]=hash[i]-hash[i-len]*powk;
	}
	sort(dis+1,dis+n-len+1);
	size=unique(dis+1,dis+n-len+1)-dis-1;
	for(i=len;i<=n;i++)
	{
		root[i]=root[i-1];
		Insert(1,size,root[i],lower_bound(dis+1,dis+size+1,hash[i]-hash[i-len]*powk)-dis);
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		for(j=1,now=0;j<=len;j++)
		{
			scanf("%d",&x);
			now=now*seed+x;
		}
		k=lower_bound(dis+1,dis+size+1,now)-dis;
		if(dis[k]!=now||query(1,size,root[l+len-2],root[r],k)==0)
			puts("Yes");
		else
			puts("No");
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值