BZOJ 2124等差子序列 线段树&&hash

【题目描述 Description】

给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列。

【输入描述 Input Description】

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

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

【输出描述 Output Description】

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

【样例输入 Sample Input】

2

3

1 3 2

3

3 2 1

【样例输出 Sample Output】

N

Y

【数据范围及提示 Data Size & Hint】

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

【解题思路】

首先声明,此题开始并没有什么思路,只找到一个O(N^2)的算法,然而这并没有什么卵用。

老师明示暗示我要我用线段树去做,我苦思冥想没有想出来,于是就抄了题解。

题解是这样的,枚举等差中项,用一颗线段树去维护那些值选了,那些值没选,构成一个01串之后求一个哈希值。

如果出现中项左边的hash值和右边的hash值不一样的情况,就说明存在等差数列,因为证明有一个值在中项左边已经选过,并且与其对应的值在中项右边还没有选。

插入O(logn),查询O(logn),扫一遍O(n)整体O(ologn);

代码略丑

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=10000+10, mod=100000007;
int xp[maxn],a[maxn],n,v,t;
long long sumv[4*maxn][2];
//sumv[i][0] 代表从左边扫的值,sumv[i][1]代表从右边扫的值
void updata(int u,int l,int r){
    int lc=u<<1,rc=lc+1;
    if (l==r) sumv[u][0]=sumv[u][1]=1;
    else{
        int mid=(l+r)/2;
        if (v<=mid) updata(lc,l,mid);
        else updata(rc,mid+1,r);
        sumv[u][0]=(sumv[rc][0]+xp[r-mid]*sumv[lc][0]%mod)%mod;
        sumv[u][1]=(sumv[lc][1]+xp[mid-l+1]*sumv[rc][1]%mod)%mod;
    }
}

long long query(int node,int l,int r,int a,int b,int x){
    int lc=node<<1,rc=lc+1;
    if (l==a&&r==b) return sumv[node][x];
    int mid=(l+r)/2;
    long long left=0,right=0;
    if (mid<b) right=query(rc,mid+1,r,max(mid+1,a),b,x);
    if (a<=mid) left=query(lc,l,mid,a,min(mid,b),x);
    return (x?left+right*xp[max(0,mid-a+1)]%mod:right+left*xp[max(0,b-mid)]%mod)%mod;
}

int main(){
    scanf("%d",&t);
    for (int ii=0;ii<t;ii++){
        memset(sumv,0,sizeof(sumv));
        bool flag=0;
        scanf("%d",&n);
        xp[0]=1;
        for (int i=1;i<=n+5;i++) xp[i]=(xp[i-1]<<1)%mod;
        for (int i=0;i<n;i++)scanf("%d",&a[i]);
        for (int i=0;i<n;i++){
            int x=a[i];
            int len=min(x-1,n-x);//长度取短之后比较
            if (len) {
                int t1=query(1,1,n,x+1,x+len,1);
                int t2=query(1,1,n,x-len,x-1,0);
                if (t1!=t2){
                flag=1;
                break;
            }
            }
            v=x;
            updata(1,1,n);
        }
        if (flag) printf("Y\n");
        else printf("N\n");
    }
}
时间: 2024-11-07 16:05:41

BZOJ 2124等差子序列 线段树&&hash的相关文章

BZOJ 2124: 等差子序列 线段树维护hash

2124: 等差子序列 Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. Output 对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”. Sample Input 2 3 1 3 2 3 3 2 1 Sample Output N Y HI

BZOJ 2124: 等差子序列

Sol 线段树+Hash. 首先暴力 等差子序列至少3项就可以了,就枚举中项,枚举公差就可以了,只需要一个数在中项前出现,另一个数在中项前没出现过就可以了.复杂度 \(O(n^2)\) 然后我想了一个虽然复杂度没变(因为我不会设计这个数据结构...) 但是好像有点用的算法,就是枚举中项,考虑从一个中项转移到另一个中项,那就是 \(\pm \Delta\) 就可以了...如果能够用数据结构维护这个操作,那就灰常好了.变换中项也就是变换折叠的位置吧. 标算呢...就是用线段树维护Hash,一个数字出

bzoj 2124 等差子序列 树状数组维护hash+回文串

等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 1919  Solved: 713[Submit][Status][Discuss] Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3), 使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数. 下接T组数据,

2124: 等差子序列 - BZOJ

Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,-ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. Output 对于每组数据,如果存在一个等差子序列,则输出一行"Y",否则输出一行"N". Sample Input 2 3 1 3 2 3 3 2 1 Sample Output

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

BZOJ_2124_等差子序列_线段树+Hash

Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3), 使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数. 下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. N<=10000,T<=7 Output 对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则

BZOJ [P2124] 等差子序列

线段树维护哈希值 要求出现长度大于三的等差子序列,我们只要找到长度等于三的就可以了 初看本题没有思路,只能暴力枚举,O(n^4) 后来发现,这个序列是n的一个排列,那么每个数字都只会出现一次 我们可以维护一个 \(01\) 序列 B ,表示某个数字是否出现过, 然后我们从左往右枚举等差中项x并将该项在B中置为1,存在等差数列当且仅当, B序列以x为中心的极大子区间不是回文子区间 我们该如何高效的判断回文子区间呢,首先维护B的正反两个序列 然后有两种做法 1.线段树维护 \(01\) 序列的哈希值

【线段树+HASH】CODEFORCES 580E Kefa and Watch

通道 题意:0-9字符串,区间修改,区间询问是否d周期 思路:直接暴力线段树,然后HASH修改和查询,卡HASH的话就双HASH. 代码: #include<cstdio> #include<cstring> typedef long long ll; const int N = 100007; int n, m, k, lens; char s[N]; #define lch id<<1 #define rch id<<1|1 class HashTree