洛谷P2757 [国家集训队]等差子序列 (hash+线段树)

题目连接

这题只要令 $len=3$看是否符合即可。因为是一个 $1$到 $n$的排列,考虑数列中项,那么对于一个数 $x$,令 $k=max(n-x, x-1)$,只要存在 $din(1,k)$,使 $x+d$和 $x-d$位于数 $x$在序列中的位置的异侧即可。进一步分析我们要做的就是从左向右扫一遍序列,每扫过一个数就将其在值域区间上打上标记,对于目前扫到的数 $x$,判断在值域区间上以它为中心的左右长度为 $k$的区域是否对称,如果对称代表在 $[1,n]$范围内所有以它为中项的能与它形成等差数列的一对值都在同侧,不满足,否则满足。判断区间相等问题不难想到hash,用线段树维护即可。

#include<cstdio>
#include<cstring>
#define min(a,b) (a)<(b) ? (a):(b)
#define ull unsigned long long
const int maxn = 1e5+50;
const int p = 998244353;

int T,n;
int a[maxn];
ull kk[maxn],hash[2][maxn<<2];
bool vis;

void pushup(int x,int len){
    int tt = len>>1;
    hash[0][x] = (hash[0][x<<1]*kk[tt]+hash[0][x<<1|1]) % p;
    hash[1][x] = (hash[1][x<<1]+hash[1][x<<1|1]*kk[len-tt]) % p;
}

void update(int x,int l,int r,int val){
    if(l == r){
        hash[0][x] = hash[1][x] = 1;
        return ;
    }
    int mid = l+r>>1;
    if(val <= mid)  update(x<<1,l,mid,val);
    else    update(x<<1|1,mid+1,r,val);
    pushup(x,r-l+1);
}

ull query(int x,int l,int r,int ql,int qr,int sta){
    if(ql > qr)     return 0;
    if(ql <= l && qr >= r)  return hash[sta][x];
    int mid = l+r>>1;
    if(qr <= mid)   return query(x<<1,l,mid,ql,qr,sta);
    else if(ql > mid)   return query(x<<1|1,mid+1,r,ql,qr,sta);
    ull dx = kk[qr-mid], dy = 1;
    if(sta)  dx = 1, dy = kk[mid-ql+1];
    return (query(x<<1,l,mid,ql,mid,sta)*dx+query(x<<1|1,mid+1,r,mid+1,qr,sta)*dy) % p;
}

int main(){
    scanf("%d",&T);  kk[0] = 1;
    for(int i = 1; i < maxn; ++i)   kk[i] = kk[i-1]*3%p;
    while(T--){
        memset(hash,0,sizeof(hash));
        scanf("%d",&n);  vis = 0;
        for(int i = 1; i <= n; ++i)  scanf("%d",&a[i]);
        for(int i = 1; i <= n; ++i){
            int q = min(a[i]-1,n-a[i]);
            if(query(1,1,n,a[i]-q,a[i]-1,0) != query(1,1,n,a[i]+1,a[i]+q,1)){
                vis = 1;  break;
            }
            update(1,1,n,a[i]);
        }
        puts(vis ? "Y":"N");
    }
    return 0;
}
你只有十分努力,才能看上去毫不费力。
原文地址:https://www.cnblogs.com/214txdy/p/14045528.html