BZOJ 3207 花神的嘲讽计划I Hash+可持久化线段树/划分树

23 篇文章 0 订阅

题目大意:给定一个数字串,多次求某个区间内有没有一个长度为k的子串

首先对字符串进行哈希 然后问题就转化成了求一个区间内有没有某个数

可持久化线段树即可 其实划分树会更快一些


※注意事项:

1.n<=200000 我找不到数据范围是眼科大夫去找老阎的关系?

2.哈希值用unsigned long long 铁则 unsigned int 会被卡掉

3.线段树那里直接x+y>>1会爆unsigned long long 转换一下 x+y>>1=(x>>1)+(y>>1)+(x&y&1)

4.Yes和No不要弄反

5.内存不够用 mempool适当开小点 刚好不MLE为止即可 不知道为什么开少一点不会RE


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 200200
#define key 10016959
using namespace std;
typedef unsigned long long ll;
struct abcd{
	abcd *ls,*rs;
	int num;
}*tree[M],mempool[M*50],*C=mempool;
int n,m,k,a[M];
ll sum[M],key_k=1;
abcd* New_Node(abcd *_,abcd *__,int ___)
{
	C->ls=_;
	C->rs=__;
	C->num=___;
	return C++;
}
abcd* Build_Tree(abcd *p,ll x,ll y,ll hash)
{
	ll mid=(x>>1)+(y>>1)+(x&y&1);
	if(x==y)
		return New_Node(0x0,0x0,p->num+1);
	if(hash<=mid)
		return New_Node(Build_Tree(p->ls,x,mid,hash),p->rs,p->num+1);
	else
		return New_Node(p->ls,Build_Tree(p->rs,mid+1,y,hash),p->num+1);
}
bool Get_Ans(abcd *p1,abcd *p2,ll x,ll y,ll hash)
{
	ll mid=(x>>1)+(y>>1)+(x&y&1);
	if(p2->num-p1->num==0)
		return 0;
	if(x==y)
		return 1;
	if(hash<=mid)
		return Get_Ans(p1->ls,p2->ls,x,mid,hash);
	else
		return Get_Ans(p1->rs,p2->rs,mid+1,y,hash);
}
int main()
{
	int i,j,x,y,z;
	bool ans;
	cin>>n>>m>>k;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]*key+a[i];
	}
	for(i=1;i<=k;i++)
		key_k*=key;
	tree[k-1]=New_Node(C,C,0);
	for(i=k;i<=n;i++)
		tree[i]=Build_Tree(tree[i-1],0ull,18446744073709551615ull,sum[i]-sum[i-k]*key_k);
	for(i=1;i<=m;i++)
	{
		ll hash=0;
		scanf("%d%d",&x,&y);
		for(j=1;j<=k;j++)
			scanf("%d",&z),hash=hash*key+z;
		if(y-x+1<k)
			ans=0;
		else
			ans=Get_Ans(tree[x+k-2],tree[y],0ull,18446744073709551615ull,hash);
		printf("%s\n",ans?"No":"Yes");
	}
}

然后是划分树版本的 快了一些

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 200200
#define key 10016959
using namespace std;
typedef unsigned long long ll;
int n,m,k,num[M],s[20][M];
ll sum[M],a[M],b[M],c[M],key_k=1;
void Build_Tree(int l,int r,int dpt)
{
	int i,mid=l+r>>1;
	int l1=l,l2=mid+1;
	int left=mid-l+1;
	if(l==r)
		return ;
	for(i=l;i<=r;i++)
		left-=a[i]<c[mid];
	for(i=l;i<=r;i++)
	{
		if(a[i]<c[mid]||a[i]==c[mid]&&left)
			b[l1++]=a[i],s[dpt][i]=i==l?1:s[dpt][i-1]+1,left-=a[i]==c[mid];
		else
			b[l2++]=a[i],s[dpt][i]=i==l?0:s[dpt][i-1];
	}
	memcpy( a+l , b+l , sizeof(a[0])*(r-l+1) );
	Build_Tree(l,mid,dpt+1);
	Build_Tree(mid+1,r,dpt+1);
}
bool Get_Ans(int l,int r,int dpt,int x,int y,ll hash)
{
	int mid=l+r>>1;
	int l1=x==l?0:s[dpt][x-1],l2=s[dpt][y];
	if(x>y)
		return 0;
	if(l==r)
		return c[mid]==hash;
	if(hash<=c[mid])
		return Get_Ans(l,mid,dpt+1,l+l1,l+l2-1,hash);
	else
		return Get_Ans(mid+1,r,dpt+1,(mid+1)+(x-l-l1),(mid+1)+(y-l+1-l2)-1,hash);
}
int main()
{
	int i,j,x,y,z;
	bool ans;
	cin>>n>>m>>k;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		sum[i]=sum[i-1]*key+num[i];
	}
	for(i=1;i<=k;i++)
		key_k*=key;
	for(i=k;i<=n;i++)
		c[i]=a[i]=sum[i]-sum[i-k]*key_k;
	sort(c+k,c+n+1);
	Build_Tree(k,n,0);
	for(i=1;i<=m;i++)
	{
		ll hash=0;
		scanf("%d%d",&x,&y);
		for(j=1;j<=k;j++)
			scanf("%d",&z),hash=hash*key+z;
		if(y-x+1<k)
			ans=0;
		else
			ans=Get_Ans(k,n,0,x+k-1,y,hash);
		printf("%s\n",ans?"No":"Yes");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值