3207: 花神的嘲讽计划Ⅰ
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3207
题意:
DJ在讲课之前会有一个长度为N方案,我们可以把它看作一个数列。
同样,花神在听课之前也会有一个嘲讽方案,有M个,每次会在x到y的这段时间开始嘲讽,为了减少题目难度,每次嘲讽方案的长度是一定的,为K。
花神嘲讽DJ让DJ尴尬需要的条件:
在x~y的时间内DJ没有讲到花神的嘲讽方案,即J的讲课方案中的x~y没有花神的嘲讽方案。
经过众蒟蒻努力,在一次讲课之前得到了花神嘲讽的各次方案,DJ得知了这个消息以后欣喜不已,DJ想知道花神的每次嘲讽是否会让DJ尴尬。
数据范围
大概是 N,M,K<=1e5
题解:
我们按照时间顺序,维护一个到 当前时间+k-1 已经出现过的,长为k的串的hash值的值域线段树。
查询只需找 第l-1棵 和 第r-k+1棵线段树 在该值位置的前后数量之差是否>0即可。
可以知道hash值(unsigned long long)的值域是0-2^64,怎么开的下呢?
其实每一次insert,最多一条链开下来,需要log值域的空间。
n次插入,即Nlog值域的空间=1e5*64。
于是,是可以开下的。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL unsigned long long
using namespace std;
#define inf 18446744073709551615UL //2^64-1
const int N=200005;
LL base=131;
struct node
{
int ls,rs,sum;
}tr[N*40];
LL mark[N];
int n,m,k,a[N],root[N],tail=0;
void insert(int pre,int &now,LL lf,LL rg,LL val)
{
now=++tail;
tr[now]=tr[pre];
tr[now].sum=tr[pre].sum+1;
if(lf==rg) return;
LL mid=(lf>>1)+(rg>>1);
if((lf&1)&&(rg&1)) mid++; //都是奇数
if(val<=mid) insert(tr[pre].ls,tr[now].ls,lf,mid,val);
if(val>mid) insert(tr[pre].rs,tr[now].rs,mid+1,rg,val);
}
int query(int pre,int now,LL lf,LL rg,LL val)
{
if(now==pre) return 0;
if(lf==rg)
return tr[now].sum-tr[pre].sum;
LL mid=(lf>>1)+(rg>>1);
if((lf&1)&&(rg&1)) mid++;
if(val<=mid) return query(tr[pre].ls,tr[now].ls,lf,mid,val);
else return query(tr[pre].rs,tr[now].rs,mid+1,rg,val);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
root[0]=tr[0].ls=tr[0].rs=0; tr[0].sum=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
mark[0]=0;
for(int i=1;i<=n;i++)
mark[i]=mark[i-1]*base+a[i];
LL s=1;
for(int i=1;i<=k;i++) s=s*base;
for(int i=1;i<=n-k+1;i++)
{
LL val=mark[i+k-1]-mark[i-1]*s;
insert(root[i-1],root[i],0,inf,val);
}
while(m--)
{
int l,r;
scanf("%d%d",&l,&r); r=r-k+1;
LL val=0;
for(int i=1;i<=k;i++)
{
int x;scanf("%d",&x);
val=val*base+x;
}
if(query(root[l-1],root[r],0,inf,val)) printf("No\n");
else printf("Yes\n");
}
return 0;
}