暑假D5 [国家集训队]等差子序列(字符串hash)

题目描述

给一个1到N的排列{Ai},询问是否存在

1p1<p2<p3<p4<p5<<pLenN(Len3)

使得Ap1,Ap2,Ap3,,ApLen是一个等差序列。

输入格式:

输入的第一行包含一个整数T,表示组数。

下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。

输出格式:

对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”。

说明

对于5%的数据,N<=100

对于30%的数据,N<=1000

对于100%的数据,N<=10000,T<=7

题解:

排列:每个数出现且仅出现一次。

考虑从左到右枚举等差中项b(数值),在在b左边的数状态记为0,b右边的数状态记为1,。

考虑如果没有等差数列,那么b+k和b-k在同侧,即同为1或0,可以构成回文串。

对于回文串的判断,可以用数值hash,正反做一遍即可。

现在考虑枚举下一个等差中项时,如何利用现在的状态。

可以用线段树存下正反hash值,每次枚举下一个就单点修改,判断就是区间查询。

区间合并就是  val左*base^(右区间长度)+val右;

查询的合并要对查询右边界和右区间边界取min

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

#define ull unsigned long long
const int maxn=100005;
const ull base=1000007;
int T,n;
int num1,num2,root1,root2;
int a[maxn],a_pos,a_l,a_r;
ull b[maxn];
struct tree{
    ull val;
    int ls,rs;
}t1[maxn<<2],t2[maxn<<2];

void update1(int rt,int len){
    t1[rt].val=t1[t1[rt].ls].val*b[len]+t1[t1[rt].rs].val;
}

void build1(int &rt,int l,int r){
    rt=++num1;
    if(l==r){
        t1[rt].val=1;
        return ;
    }
    int mid=(l+r)>>1;
    build1(t1[rt].ls,l,mid);
    build1(t1[rt].rs,mid+1,r);
    update1(rt,r-mid);
}

void update2(int rt,int len){
    t2[rt].val=t2[t2[rt].ls].val*b[len]+t2[t2[rt].rs].val;
}

void build2(int &rt,int l,int r){
    rt=++num2;
    if(l==r){
        t2[rt].val=1;
        return ;
    }
    int mid=(l+r)>>1;
    build2(t2[rt].ls,l,mid);
    build2(t2[rt].rs,mid+1,r);
    update2(rt,r-mid);
}

void modify1(int rt,int l,int r){
    if(l==r){
        t1[rt].val=0;
        return ;
    }
    int mid=(l+r)>>1;
    if(a_pos<=mid) modify1(t1[rt].ls,l,mid);
    else modify1(t1[rt].rs,mid+1,r);
    update1(rt,r-mid);
}

void modify2(int rt,int l,int r){
    if(l==r){
        t2[rt].val=0;
        return ;
    }
    int mid=(l+r)>>1;
    if(a_pos<=mid) modify2(t2[rt].ls,l,mid);
    else modify2(t2[rt].rs,mid+1,r);
    update2(rt,r-mid);
}

ull querry1(int rt,int l,int r){
    if(a_l<=l&&r<=a_r) return t1[rt].val;
    int mid=(l+r)>>1;
    if(a_r<=mid) return querry1(t1[rt].ls,l,mid);
    else if(a_l>mid) return querry1(t1[rt].rs,mid+1,r);
    else return querry1(t1[rt].ls,l,mid)*b[min(a_r,r)-mid]+querry1(t1[rt].rs,mid+1,r);
}

ull querry2(int rt,int l,int r){
    if(a_l<=l&&r<=a_r) return t2[rt].val;
    int mid=(l+r)>>1;
    if(a_r<=mid) return querry2(t2[rt].ls,l,mid);
    else if(a_l>mid) return querry2(t2[rt].rs,mid+1,r);
    else return querry2(t2[rt].ls,l,mid)*b[min(a_r,r)-mid]+querry2(t2[rt].rs,mid+1,r);
}

void nice(){
    num1=num2=root1=root2=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build1(root1,1,n);
    build2(root2,1,n);
    for(int i=2;i<n;i++){
        a_pos=a[i-1];
        modify1(root1,1,n);
        a_pos=n-a[i-1]+1;
        modify2(root2,1,n);
        ull ret1,ret2;
        int len=min(a[i]-1,n-a[i]);
        if(!len) continue;
        a_r=a[i]-1;a_l=a[i]-len;
        ret1=querry1(root1,1,n);
        a_r=n-a[i];a_l=a_r-len+1;
        ret2=querry2(root2,1,n);
        if(ret1!=ret2) {printf("Y
");return ;}
    }
    printf("N
");
}

int main(){
    b[0]=1;
    for(int i=1;i<=100000;i++) b[i]=b[i-1]*base;
    scanf("%d",&T);
    while(T--) nice();
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11185587.html