bzoj 3207: 花神的嘲讽计划Ⅰ (主席树+hash)

题目描述

传送门

题解

对于每个位置的权值为[i,i+k-1]这一段的hash值。
离散化之后依次插入主席树中。
查询的时候先找到这一段离散后的值,然后查询[x,y-k+1]这一段中是否出现过。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<map>
#define ull unsigned long long 
#define p 2000001001
#define N 100003
using namespace std;
struct data{
    int l,r,sum;
}tr[N*20];
map<ull,int> mp;
int sz,root[N],a[N],n,m,k,cnt;
ull mi[N],val[N],b[N];
void insert(int &i,int j,int l,int r,int x)
{
    i=++sz; tr[i]=tr[j];
    tr[i].sum++;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) insert(tr[i].l,tr[j].l,l,mid,x);
    else insert(tr[i].r,tr[j].r,mid+1,r,x);
}
int qjsum(int i,int j,int l,int r,int x)
{
    if (l==r) return tr[j].sum-tr[i].sum;
    int mid=(l+r)/2;
    if (x<=mid) return qjsum(tr[i].l,tr[j].l,l,mid,x);
    else return qjsum(tr[i].r,tr[j].r,mid+1,r,x);
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    mi[0]=1;
    for (int i=1;i<=k;i++) mi[i]=mi[i-1]*p;
    for (int i=1;i<=n-k+1;i++) {
        val[i]=0;
        for (int j=1;j<=k;j++)
         val[i]+=mi[j]*(ull)a[i+j-1];
        ///cout<<val[i]<<endl;
        b[i]=val[i];
    }
    sort(b+1,b+n+1);
    cnt=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n-k+1;i++) {
     a[i]=lower_bound(b+1,b+cnt+1,val[i])-b;
     mp[val[i]]=a[i];
    }
    //for (int i=1;i<=n-k+1;i++) cout<<a[i]<<" "; cout<<endl;
    for (int i=1;i<=n-k+1;i++) insert(root[i],root[i-1],1,n,a[i]); 
    for (int i=1;i<=m;i++) {
        int x,y; scanf("%d%d",&x,&y);
        ull t=0;
        for (int j=1;j<=k;j++) {
            int c; scanf("%d",&c);
            t+=(ull)mi[j]*(ull)c;
        }
        //cout<<t<<endl;
        if (!mp[t]||y-x+1<k) {
            printf("Yes\n");
            continue;
        }
        //cout<<mp[t]<<endl;
        if (qjsum(root[x-1],root[y-k+1],1,n,mp[t])) printf("No\n");
        else printf("Yes\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值