3207: 花神的嘲讽计划Ⅰ

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3207

题解

  Debug了一上午,最后终于知道wa的原因了:

  智障选手wyw交错了题目。


  先把所有长度为K的串哈希出来,然后以哈希为第一关键字,序号为第二关键字排序。每次询问的串,把它的哈希也求出来,假设是h,那就看一下你原串中有没有hash为h的串,没有就直接输出Yes,否则找到对应的区间。因为已经排了序,所以其序号是单调的,现在问题就成了,看看这些串里有没有一个序号介于[l,r-K+1]的。二分查找第一个大于等于l的即可。
  呃也可以主席树,那就搞一个权值主席树,每次直接查询[l,r]的区间里权值为h的这个点上有没有数就好了…水

代码

//哈希+二分查找
#include <cstdio>
#include <algorithm>
#include <map>
#define ll long long
#define maxn 100010
#define base 10007ll
#define inf 0x3f3f3f3f
using namespace std;
ll N, M, h[maxn], K, num[maxn], a[maxn];
map<ll,ll> begin, end;
inline ll read(ll x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x;
}
inline bool cmp(ll a, ll b){if(h[a]==h[b])return a<b;return h[a]<h[b];}
inline void init()
{
    ll i, mi, t=0;
    N=read(), M=read(), K=read();
    for(i=1;i<=N;i++)a[i]=read();
    for(i=1;i<=K;i++)t=t*base+a[i];
    for(i=1,mi=1;i<=K;i++)mi=mi*base;
    h[1]=t;
    for(i=2;i+K-1<=N;i++)
    {
        t=t*base+a[i+K-1]-a[i-1]*mi;
        h[i]=t;
    }
    for(i=1;i+K-1<=N;i++)num[i]=i;
    sort(num+1,num+N-K+1 +1,cmp);
    for(i=2;i+K-1<=N;i++)
        if(h[num[i]]!=h[num[i-1]])end[h[num[i-1]]]=i-1,begin[h[num[i]]]=i;
    begin[h[num[1]]]=1;end[h[num[N-K+1]]]=N-K+1;
}
inline ll find(ll L, ll R, ll x)
{
    ll l, r, mid;
    for(l=L,r=R;l<r;)
    {
        mid=(l+r)>>1;
        if(num[mid]<x)l=mid+1;
        else r=mid;
    }
    if(num[l]>=x)return num[l];
    return inf;
}
inline void work()
{
    ll l, r, h, i, x, t=0;
    for(;M;M--)
    {
        t++;
        l=read(), r=read()-K+1;
        for(i=1,h=0;i<=K;i++)h=h*base+read();
        if(begin.find(h)==begin.end()){printf("Yes\n");continue;}
        x=find(begin[h],end[h],l);
        if(x<=r)printf("No\n");
        else printf("Yes\n");
    }
}
int main()
{
    init();
    work();
}
//主席树+哈希
#include <cstdio>
#include <algorithm>
#include <map>
#define ll long long
#define base 10007
#define maxn 100010
using namespace std;
ll N, M, K, ndtot, h[maxn], tot, a[maxn];
struct segtree
{
    ll l, r, cnt;
    segtree *ch[2];
}pool[maxn*20], *root[maxn];
map<ll,ll> table;
inline ll read(ll x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x;
}
void ins(segtree *pre, segtree *now, ll pos)
{
    ll mid=(pre->l+pre->r)>>1;
    *now=*pre;
    if(now->l==now->r){now->cnt++;return;}
    if(pos<=mid)ins(pre->ch[0],now->ch[0]=pool+ ++ndtot,pos);
    else ins(pre->ch[1],now->ch[1]=pool+ ++ndtot,pos);
}
void build(segtree *p, ll l, ll r)
{
    ll mid=(l+r)>>1;
    p->l=l, p->r=r;
    if(l==r)return;
    build(p->ch[0]=pool+ ++ndtot,l,mid);
    build(p->ch[1]=pool+ ++ndtot,mid+1,r);
}
void init()
{
    ll i, t, mi;
    N=read(), M=read(), K=read();
    for(i=1;i<=N;i++)a[i]=read();
    for(i=1,t=0;i<=K;i++)t=t*base+a[i];
    h[1]=t;
    for(i=1,mi=1;i<=K;i++)mi*=base;
    for(i=2;i+K-1<=N;i++)h[i]=t=t*base+a[i+K-1]-a[i-1]*mi;
    for(i=1;i+K-1<=N;i++)if(table.find(h[i])==table.end())table[h[i]]=++tot;
    build(root[0]=pool+ ++ndtot,1,tot);
    for(i=1;i<=N-K+1;i++)ins(root[i-1],root[i]=pool+ ++ndtot,table[h[i]]);
}
ll find(segtree *L, segtree *R, ll pos)
{
    ll mid=(L->l+L->r)>>1;
    if(L->l==L->r)return R->cnt-L->cnt;
    if(pos<=mid)return find(L->ch[0],R->ch[0],pos);
    else return find(L->ch[1],R->ch[1],pos);
}
void work()
{
    ll l, r, x, h, i;
    while(M--)
    {
        l=read(), r=read()-K+1;
        for(i=1,h=0;i<=K;i++)x=read(),h=h*base+x;
        if(table.find(h)==table.end()){printf("Yes\n");continue;}
        h=table[h];
        if(find(root[l-1],root[r],h))printf("No\n");
        else printf("Yes\n");
    }
}
int main()
{
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值