bzoj 3207 可持久化线段树

  首先因为固定询问长度,所以我们可以将整个长度为n的数列hash成长度为n-k+1的数列,每次询问的序列也hash成一个数,然后询问这个数是不是在某个区间中出现过,这样我们可以根据初始数列的权值建立可持久化线段树,对于每个询问先二分判断是否出现在数列中过,然后再判断是否在区间内出现过。也可以离线将询问和数列建立可持久化线段树,那么直接判断就可以了,但是空间消耗会大些。

  反思:偷懒直接用的map判的是否出现,然后改了hash用long long存之后map_insert没有改,然后就一直WA。

     不知道数据的范围,开大了数组好多。

/**************************************************************

    Problem: 3207

    User: BLADEVIL

    Language: C++

    Result: Accepted

    Time:6580 ms

    Memory:130804 kb

****************************************************************/

 

//By BLADEVIL

#include <map>

#include <cstdio>

#include <cstring>

#include <algorithm>

#define maxn 500010

#define LL long long

  

using namespace std;

   

struct segment {

    int left,right,cnt;

    int son[2];

    segment() {

        left=right=cnt=0;

        memset(son,0,sizeof son);

    }

}t[12*maxn];

   

struct rec {

    int num,key;

    LL hash;

    rec() {

        hash=num=key=0;

    }

}a[maxn];

   

int n,m,k,sum,tot;

int rot[maxn];

map<LL,int>tree;

   

bool cmp1(rec x,rec y) {

    return x.hash<y.hash;

}

   

bool cmp2(rec x,rec y) {

    return x.num<y.num;

}

   

void map_insert(LL x,int y) {

    if (tree.find(x)==tree.end()) tree.insert(pair<LL,int>(x,y));

}

   

void build(int &x,int l,int r) {

    if (!x) x=++tot;

    t[x].left=l; t[x].right=r;

    if (l==r) return ;

    int mid=t[x].left+t[x].right>>1;

    build(t[x].son[0],l,mid); build(t[x].son[1],mid+1,r);

}

   

void insert(int &x,int rot,int y) {

    if (!x) x=++tot;

    t[x].left=t[rot].left; t[x].right=t[rot].right;

    if (t[x].left==t[x].right) {

        t[x].cnt=t[rot].cnt+1;

        return ;

    }

    int mid=t[x].left+t[x].right>>1;

    if (y>mid) {

        t[x].son[0]=t[rot].son[0];

        insert(t[x].son[1],t[rot].son[1],y);

    } else {

        t[x].son[1]=t[rot].son[1];

        insert(t[x].son[0],t[rot].son[0],y);

    }

    t[x].cnt=t[rot].cnt+1;

}

   

int query(int lx,int rx,int y) {

    if (t[lx].left==t[lx].right) return t[rx].cnt-t[lx].cnt;

    int mid=t[lx].left+t[lx].right>>1;

    if (y>mid) return query(t[lx].son[1],t[rx].son[1],y); else return query(t[lx].son[0],t[rx].son[0],y);

}

   

int main() {

    scanf("%d%d%d",&n,&m,&k);

    for (int i=1;i<=n;i++) scanf("%d",&a[i].key);

    n=n-k+1;

    for (int i=1;i<=n;i++) 

        for (int j=i;j<=i+k-1;j++) a[i].hash=a[i].hash*107+a[j].key;

    for (int i=1;i<=n;i++) a[i].num=i;

    sort(a+1,a+n+1,cmp1); sum=1; LL cur=a[1].hash;

    //for (int i=1;i<=n;i++) printf("%d ",a[i].hash); printf("\n");

    for (int i=1;i<=n;i++)

        if (a[i].hash==cur) a[i].key=sum; else cur=a[i].hash,a[i].key=++sum;

    sort(a+1,a+n+1,cmp2);

    //for (int i=1;i<=n;i++) printf("%d ",a[i].key); printf("\n"); 

    for (int i=1;i<=n;i++) map_insert(a[i].hash,a[i].key);

    build(rot[0],1,sum);

    for (int i=1;i<=n;i++) insert(rot[i],rot[i-1],a[i].key);

    while (m--) {

        int l,r; scanf("%d%d",&l,&r); if (r>n) r=n;

        LL ha=0;

        for (int i=1;i<=k;i++) {

            int x; scanf("%d",&x);

            ha=ha*107+x;

        }

        map<LL,int>::iterator p=tree.find(ha);

        if (p==tree.end()) {

            printf("Yes\n");

            continue;

        }

        int y=p->second;

        if (query(rot[l-1],rot[r],y)>0) printf("No\n"); else printf("Yes\n");

        //printf("%d\n",query(rot[l-1],rot[r],y));

    }

    return 0;

}

 

你可能感兴趣的:(线段树)