BZOJ 3207: 花神的嘲讽计划Ⅰ|莫队|主席树|hash

9 篇文章 0 订阅
4 篇文章 0 订阅

本来以为会卡主席树内存,然后写了莫队T到死,发现想多了……
然而这题并不卡内存,直接主席树做就好了。
先给长度为K的序列hash一下然后就变成了询问一个区间内有没有某一个数,我离散化了一下,然后改成主席树发现又T到死……woc…终于发现二分查找有鬼..
然后就发现似乎莫队和主席树的二分都是一样的都有鬼..改了一下发现都能过,而且,而且莫队比主席树要快!!!!!!!!!!
发现主席树似乎并不需要离线离散化,只要强行扩大保存的区间的范围就好,因为复杂度是 log 的所以强行扩大范围的话复杂度多不了多少
code,主席树

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
#define N 500022
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
struct W{int l,r,pos;ll v;}A[N];
struct U{int pos;ll v;}h[N];
ll hash[N],a[N],bin[N];
int sum[N],v[N],ans[N],root[N];
int n,m,k,tot,cnt;
int ch[5001000][2],sm[5001000];
bool cmp(U a,U b)
{
    return a.v<b.v;
}   
void add(int pre,int &x,int l,int r,int v)
{
    if(!x)x=++cnt;
    sm[x]=sm[pre]+1;
    if(l==r)return;
    int mid=l+r>>1;
    if(v>mid)
        ch[x][0]=ch[pre][0],
        add(ch[pre][1],ch[x][1],mid+1,r,v);
    else
        ch[x][1]=ch[pre][1],
        add(ch[pre][0],ch[x][0],l,mid,v);
}
int ask(int L,int R,int v)
{
    int l=1,r=tot;
    while(l!=r)
    {
        int mid=l+r>>1,e=(v>mid);
        e?l=mid+1:r=mid;
        L=ch[L][e],R=ch[R][e];
        if(sm[R]-sm[L]==0)return 0;
    }
    return sm[R]-sm[L];
}
int main()
{
    n=sc(),m=sc(),k=sc();bin[0]=1;
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i-1]*233+sc();
        bin[i]=bin[i-1]*233;
    }
    for(int i=k;i<=n;i++)
        h[i-k+1].v=a[i]-a[i-k]*bin[k],
        h[i-k+1].pos=i-k+1;
    n=n-k+1;
    sort(h+1,h+n+1,cmp);
    for(int i=1;i<=n;i++)
        v[h[i].pos]=(h[i].v==h[i-1].v?tot:++tot),
        hash[tot]=h[i].v;
    for(int i=1;i<=n;i++)
        add(root[i-1],root[i],1,tot,v[i]);
    for(int i=1;i<=m;i++)
    {
        A[i].l=sc();
        A[i].r=sc()-k+1;
        for(int j=1;j<=k;j++)A[i].v=A[i].v*233+sc();
        int x=lower_bound(hash+1,hash+tot+1,A[i].v)-hash;
        if(hash[x]!=A[i].v||A[i].r<A[i].l)puts("Yes");
        else
        {
            if(ask(root[A[i].l-1],root[A[i].r],x)) puts("No");
            else puts("Yes");
        }
    }
    return 0;
}

code,莫队

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
#define N 500022
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
struct W{int l,r,pos;ll v;}A[N];
struct U{int pos;ll v;}h[N];
ll hash[N],a[N],bin[N];
int sum[N],bl[N],v[N],ans[N];
int n,m,k,block,tot;
bool cmp(U a,U b)
{
    return a.v<b.v;
}
bool cmp1(W a,W b)
{
    return bl[a.l]==bl[b.l]?a.r<b.r:bl[a.l]<bl[b.l];
}
int find(ll x)
{
    int l=1,r=tot;
    while(l<r)
    {
        int mid=l+r>>1;
        if(hash[mid]==x)return mid;
        else if(hash[mid]<x) l=mid+1;
        else r=mid-1;
    }
    return hash[l]==x?l:0;
}       
int main()
{
    n=sc(),m=sc(),k=sc();block=sqrt(n);bin[0]=1;
    for(int i=1;i<=n;i++)
    {
        bl[i]=(i-1)/block+1;
        a[i]=a[i-1]*233+sc();
        bin[i]=bin[i-1]*233;
    }
    for(int i=k;i<=n;i++)
        h[i-k+1].v=a[i]-a[i-k]*bin[k],
        h[i-k+1].pos=i-k+1;
    n=n-k+1;
    sort(h+1,h+n+1,cmp);
    for(int i=1;i<=n;i++)
        v[h[i].pos]=(h[i].v==h[i-1].v?tot:++tot),
        hash[tot]=h[i].v;
    for(int i=1;i<=m;i++)
    {
        A[i].l=sc();
        A[i].r=sc()-k+1;
        A[i].pos=i;
        for(int j=1;j<=k;j++)A[i].v=A[i].v*233+sc();
    }
    sort(A+1,A+m+1,cmp1);
    int l=1,r=0;
    for(int i=1;i<=m;i++)
    {
        int x=find(A[i].v);
        if(!x||A[i].r<A[i].l)continue;
        while(l<A[i].l)sum[v[l++]]--;
        while(l>A[i].l)sum[v[--l]]++;
        while(r>A[i].r)sum[v[r--]]--;
        while(r<A[i].r)sum[v[++r]]++;
        if(sum[x])ans[A[i].pos]=1;
    }
    for(int i=1;i<=m;i++)ans[i]?puts("No"):puts("Yes");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值