[Luogu P5679][GZOI2017]等差子序列

\(GZOI2017D2T2\)

题目链接?不存在的Luogu P5679 [GZOI2017]等差子序列

题面

首先,题目可以转化为是否存在长度为\(3\)的等差子序列。

枚举中间的那个数\(a_i\),判断两边是否存在两个数和中间这个数组成等差数列。

枚举差值\(d\),判断是否存在\(a_i-d,a_i+d\),这一步可以bitset加速。

但是还是比较慢,数据挺水,不知道能不能卡TLE。。

时间复杂度 \(O(T\frac{n^2}{32})\)

代码:

#include <cstdio>
#include <bitset>
#include <cstring>
#include <algorithm>
#define Sub(B,l,r) (B>>(l)&One[(r)-(l)])

const int N=20005;
int T,n,a[N],c[N];
std::bitset<N> Pre,Suf,One[N];

int main()
{
    One[0].set(0);
    for(int i=1;i<=20000;++i)(One[i]=One[i-1]).set(i);
    for(scanf("%d",&T);T--;)
    {
        scanf("%d",&n),Pre.reset(),Suf.reset(),memset(c,0,sizeof c);
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)++c[a[i]],Suf.set(a[i]);
        bool Flag=false;
        for(int i=1;i<=n;++i)
        {
            if(!--c[a[i]])Suf.reset(a[i]);
            int l=std::min(a[i]-1,20000-a[i]);
            if((Sub(Pre,20001-a[i]-l,20001-a[i]+l)&Sub(Suf,a[i]-l,a[i]+l)).count())Flag=true,i=n;
            Pre.set(20001-a[i]);
        }
        puts(Flag?"YES":"NO");
    }
    return 0;
}

Update in 2019/11/21

感谢UOJ群的聚聚们orz,这怎么出原题啊。。

[bzoj3509][CodeChef]COUNTARI

是不是都喜欢用权限题啊

首先题目就相当于求是否存在\((1\le i<j<k\le n,2a_j=a_i+a_k)\)(这题还削弱了,原题求数量)

枚举\(a_j\),则将两边看成一个生成函数卷积起来,最后看\(2a_j\)这个位置是不是\(0\),不是则存在答案。

直接做就是\(O(n*n\log n)\)的,还不如暴力,那么就要优化一下:

对数列进行分块,设块大小为\(B\),那么分两种情况:

  • \(3\)个数在不同块中:

枚举\(a_j\)所在块,对两边进行卷积,用这个块中所有数都当成\(a_j\)判断一遍。

复杂度 \(O(\frac nB *2^{16}*16)\)(值域为\(2^{16}\)

  • 至少两个数在一个块中

枚举这个块,在块中枚举两个数,同时维护一下左右的数字个数,查询一下就好。

时间复杂度 \(O(\frac nB*B^2)=O(nB)\)

\(\frac nB *2^{16}*16=nB\),解得\(B=1024\)

但因为\(FFT\)常数较大,将块大小开大一点可能更快。

时间复杂度 \(O(TnB)\)(还不如bitset

代码网上随便找一份就好了

可惜我不会D2T3,要不然就能AK一套省选了(小声(虽然是弱省(其实没读懂题放弃了

posted @ 2019-11-20 21:38  LanrTabe  阅读(334)  评论(0编辑  收藏  举报