• codevs 1283 等差子序列


    http://codevs.cn/problem/1283/

    题目描述 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

    线段树+hash

    首先要注意的是这个排列是1到n的排列

    然后当然是找3个数形成等差子序列

    暴力:枚举中间的数,枚举左边的数,枚举右边的数,看是否满足 2*mid=l+r

    O(n³)

    继续想,因为保证排列是1到n

    所以对于一个数x,若以x为mid能形成等差子序列,那么另外两个数一定在x两侧

    即从左往右枚举,当枚举到mid时,能早就枚举到了l,不能枚举到r

    可以用0,1表示这个数是否被枚举到

    举个例子:

    3 6 1 2 4 5

    当枚举到第5个数4时,0 1序列为

    1 1 1 1 0 1

    4的左边分别是1和0,说明枚举到4时,3已经被枚举到了,5还没有被枚举

    但这样仍然要枚举,没有减少时间复杂度

    如何去掉枚举的过程?

    继续想,发现我们要比较的是mid左右的两个对称区间

    举个例子:

    1 8 3 6 5 7 4 2

    当枚举到3时,0 1序列为:

    1 0 1 0 0 0 0 1

    我们实际需要的是判断2和4的01序列是否相等,1和5的01序列是否相等

    因为是对称的

    可以转化为判断区间[1,2]和区间[5,4](注意这里是[5,4],不是[4,5])是否相等

    线段树维护区间正序哈希值和倒序哈希值,即可log判断

    总复杂度:O(nlogn)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 10001
    #define LL unsigned long long
    using namespace std;
    int T,n,x,len;
    bool ok;
    LL bit[N],hash[N*4],anti_hash[N*4],r1,r2;
    struct TREE
    {
        public:
            void up(int k,int l,int r)
            {
                hash[k]=hash[k<<1]*bit[r-(l+r>>1)]+hash[k<<1|1];
                anti_hash[k]=anti_hash[k<<1|1]*bit[(l+r>>1)-l+1]+anti_hash[k<<1];
            }
            void change(int k,int l,int r,int pos)
            {
                if(l==r) 
                {
                    anti_hash[k]=hash[k]=1;
                    return;
                }
                int mid=l+r>>1;
                if(pos<=mid) change(k<<1,l,mid,pos);
                else change(k<<1|1,mid+1,r,pos);
                up(k,l,r);
            }
            LL query(int k,int l,int r,int opl,int opr,int w)
            {
                if(l>=opl&&r<=opr) return w==1 ? hash[k]:anti_hash[k];
                int mid=l+r>>1;
                if(opr<=mid) return query(k<<1,l,mid,opl,opr,w);
                else if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr,w);
                else if(w==1) return query(k<<1,l,mid,opl,mid,w)*bit[opr-mid]+query(k<<1|1,mid+1,r,mid+1,opr,w);
                else return query(k<<1|1,mid+1,r,mid+1,opr,w)*bit[mid-opl+1]+query(k<<1,l,mid,opl,mid,w);
            }
            void solve(int i)
            {
                len=min(i-1,n-i);
                r1=query(1,1,n,i-len,i-1,1);
                r2=query(1,1,n,i+1,i+len,2);
                if(r1!=r2) ok=true;
            }
    }tree;
    int main()
    {
        bit[1]=233; for(int i=2;i<N;i++) bit[i]=bit[i-1]*233;
        scanf("%d",&T);
        while(T--)
        {
            memset(hash,0,sizeof(hash));
            memset(anti_hash,0,sizeof(anti_hash));
            scanf("%d",&n);
            ok=false;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&x);
                if(!ok)
                {
                    tree.change(1,1,n,x);
                    if(x!=1&&x!=n) tree.solve(x);
                }
            }
            if(ok) puts("Y");
            else puts("N");
        }
    }
  • 相关阅读:
    sp3485在rk3288上的应用
    wk2124 在 rk3288 上的适配与调试
    Android自定义View实现一个状态切换开关
    SQLite数据库入门
    Android如何动态申请应用权限?
    APK 如何实现应用热更新功能?
    Android 如何通过代码安装 APK?
    Android.mk 中如何拷贝任意文件
    Linux 下网络 IO 的多路复用
    Android hideSoftInputFromWindow 不能隐藏软键盘怎么办?
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6813049.html
Copyright © 2020-2023  润新知