bzoj3207: 花神的嘲讽计划Ⅰ( 主席树 (动态开点) + hash )

83 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值