CF452F Permutations/Luogu2757 等差子序列 树状数组、Hash

传送门——Luogu 传送门——Codeforces



这种题目还能跟哈希扯上关系也是很神了……

如果存在长度\(>3\)的等差子序列,那么一定存在长度\(=3\)的等差子序列,所以我们只需要找长度为\(3\)的等差子序列。可以枚举等差子序列的第二个元素\(b\),那么存在长度为\(3\)的等差子序列等价于:可以在\(b\)左边找到一个元素\(a\),在\(b\)右边找到一个元素\(c\),满足\(b - a = c - b\)。

对于找到\(ac\)两个元素,一个比较直观的想法是:对\(b\)左边和右边的所有元素各建一个bitset\(B1,B2\),对于某一个元素\(d \neq b\),如果\(d\)在\(b\)左边,那么\(B1[d]=1\),否则\(B2[d]=1\)。

不存在等差子序列意味着如果\(d\)在左边,则\(2 \times b - d\)一定不能在右边,反之同理。这等价于对于两个区间\([l,b-1](l \geq 1),[b + 1 , r](r \leq N)\),在满足\(b - l = r - b\)时,\(B1[l,b-1] \lor rev(B2[b+1 , r]) = 2^{b - l} - 1\)。这是因为如果计算结果的第\(d\)位结果为\(0\),那么\(B1[l,b-1]\)和\(rev(B2[b+1 , r])\)的第\(d\)位要么同时为\(0\),要么同时为\(1\)。如果同时为\(0\)则意味着\(b-d\)不在左边且\(b+d\)不在右边,同时为\(1\)意味着\(b-d\)在左边且\(b+d\)在右边,就存在等差子序列了。

这样我们就可以从右往左枚举\(b\)的位置,动态维护\(B1,B2\)并查询。但是当数据范围到\(3 \times 10^5\)的时候bitset比较麻烦了,但是可以使用Hash+树状数组维护与上面bitset意义相同的01串。复杂度变为\(O(nlogn)\)。

虽然CF是神机,但仍然需要注意常数。

//Luogu2757
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<algorithm>
#include<cstring>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)){
    a = a * 10 + c - 48;
    c = getchar();
    }
    return a;
}

#define st first
#define nd second
struct PII{
    int first , second;
    PII(int x = 0 , int y = 0):first(x) , second(y){}
    bool operator ==(PII a){return a.st == first && a.nd == second;}
    bool operator !=(PII a){return !(*this == a);}
};
const int MAXN = 1e4 + 7 , Base = 131 , MOD1 = 1e9 + 7 , MOD2 = 1e9 + 9;
PII powBs[MAXN] , sum[MAXN] , powInv[MAXN];
int arr[MAXN] , N , T;
bool f;

PII operator *(PII a , PII b){
    return PII(1ll * a.st * b.st % MOD1 , 1ll * a.nd * b.nd % MOD2);
}

PII operator +(PII a , PII b){
    PII t(a.st + b.st , a.nd + b.nd);
    if(t.st >= MOD1) t.st -= MOD1;
    if(t.nd >= MOD2) t.nd -= MOD2;
    return t;
}

PII operator -(PII a , PII b){return a + PII(MOD1 - b.st , MOD2 - b.nd);}

struct BIT{
#define lowbit(x) (x & -x)
    PII arr[MAXN];
    BIT(){memset(arr , 0 , sizeof(PII) * (N + 1));}

    void add(int pos , PII cur){
    while(pos <= N){
        arr[pos] = arr[pos] + cur;
        pos += lowbit(pos);
    }
    }

    PII get(int pos){
    PII sum = PII(0 , 0);
    while(pos){
        sum = sum + arr[pos];
        pos -= lowbit(pos);
    }
    return sum;
    }
}Tree1 , Tree2;

inline int poww(long long a , int b , int MOD){
    int times = 1;
    while(b){
    if(b & 1) times = times * a % MOD;
    a = a * a % MOD;
    b >>= 1;
    }
    return times;
}

void init(){
    powBs[0] = sum[0] = powInv[0] = PII(1 , 1);
    for(int i = 1 ; i <= 10000 ; ++i){
    powBs[i] = powBs[i - 1] * PII(Base , Base);
    sum[i] = sum[i - 1] + powBs[i];
    }
    powInv[1] = PII(poww(Base , MOD1 - 2 , MOD1) , poww(Base , MOD2 - 2 , MOD2));
    for(int i = 2 ; i <= 10000 ; ++i)
    powInv[i] = powInv[i - 1] * powInv[1];
}

void work(){
    for(int i = 1 ; i <= N ; ++i)
    Tree1.add(arr[i] , powBs[arr[i]]);
    for(int i = N ; i ; --i){
    Tree1.add(arr[i] , PII(0 , 0) - powBs[arr[i]]);
    if(arr[i] != 1 && arr[i] != N){
        int l1 = 1 , r1 = arr[i] - 1 , l2 = arr[i] + 1 , r2 = N;
        if(r2 - l2 < r1 - l1) l1 = r1 - (r2 - l2);
        else r2 = l2 + (r1 - l1);
        PII t = (Tree1.get(r1) - Tree1.get(l1 - 1)) * powInv[l1] + (Tree2.get(N + 1 - l2) - Tree2.get(N - r2)) * powInv[N - r2 + 1];
        if(t != sum[r1 - l1]){
        f = 1;
        return;
        }
    }
    Tree2.add(N - arr[i] + 1 , powBs[N - arr[i] + 1]);
    }
}

signed main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    init();
    for(int T = read() ; T ; --T){
    N = read();
    f = 0;
    for(int i = 1 ; i <= N ; ++i)
        arr[i] = read();
    Tree1 = BIT(); Tree2 = BIT();
    work();
    puts(f ? "Y" : "N");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Itst/p/10629524.html

时间: 2024-07-30 03:56:05

CF452F Permutations/Luogu2757 等差子序列 树状数组、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组数据,

bzoj 1669: [Usaco2006 Oct]Hungry Cows饥饿的奶牛【dp+树状数组+hash】

最长上升子序列.虽然数据可以直接n方但是另写了个nlogn的 转移:f[i]=max(f[j]+1)(a[j]<a[i]) O(n^2) #include<iostream> #include<cstdio> using namespace std; const int N=5005; int n,a[N],f[N],ans; int read() { int r=0,f=1; char p=getchar(); while(p>'9'||p<'0') { if(

AC dreamoj 1011 树状数组+hash维护字符串的前缀和

http://acdream.info/problem?pid=1019 Problem Description Now we have a long long string, and we will have two kinds of operation on it. C i y : change the ith letter to y. Q i j : check whether the substring from ith letter to jth letter is a palindr

bzoj2124 等差子序列(树状数组+hash)

题意 给你一个1~n排列,问有没有一个等差数列(长度至少为3) 题解 我居然自己想到了正解. 但我最后写挂了,所以我又看了题解. 我们维护了一个以权值为下标的01序列. 我们扫描整个序列.对于每一个正在扫描的数,我们判断以这个数的权值作为对称点,01序列是否对称. 这个序列用权值树状数组维护就行. 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #

bzoj3173 最长上升子序列 树状数组

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3173 题意:向序列中动态插入$1~n$排列元素,求出插入每个元素后最长上升子序列长度. 如Claris所言,面对这种数据结构,必有高论.如果只想着数据结构,我们可以通过平衡树动态维护序列,同时使用树状数组计算最长上升子序列. 但是我们不是猩猩不是数据结构狂人,我们毕竟还是要本着能不上树就不上树能少用数据结构就少用的原则来设计算法的. 重新考虑这个题目.本题没有要求强制在线,于是我们把整个操作

hdu 5773 The All-purpose Zero 最长上升子序列+树状数组

题目链接:hdu 5773 The All-purpose Zero 官方题解:0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的. 因此我们可以把0拿出来,对剩下的做O(nlogn)的LIS,统计结果的时候再算上0的数量. 为了保证严格递增,我们可以将每个权值S[i]减去i前面0的个数,再做LIS,就能保证结果是严格递增的. 个人看法:对于显然把所以0放进去部分我解释一下: 如果0位于最长上升子序列两边,这两个零要加进去是显然的 如果有一个0夹于最长上升子序列之间,那么

[bzoj2124]等差子序列(hash+树状数组)

我又来更博啦 2124: 等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 941  Solved: 348[Submit][Status][Discuss] Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. O

HDOJ 5338 ZZX and Permutations 线段树+树状数组

[题意]: 给一个排列加上表示循环的括号,问如何让1到n的对应的字典序最大. 从1开始贪心每个数字可以往三个地方走,右边第一个,跳转到左边的某一个,和自己构成循环 对于走到右边第一个的情况,只要判断右边的那个有没有被占据就可以了,如果可以和右边的互换,那么需要在线段树中将右边的数置为0 跳转到左边的某一个,一个数如果跳转到左边的某一个则说明左边的那个是括号开头这个数是括号结尾,用一个线段树查询区间里的最大值,由于括号间是不能重叠的,所以需要用树状数组二分一下左边界.如果这个数是要跳转到左边的某个

线段树+树状数组+贪心 HDOJ 5338 ZZX and Permutations

题目传送门 1 /* 2 题意:不懂... 3 线段树+树状数组+贪心:贪心从第一位开始枚举,一个数可以是循环节的末尾或者在循环节中,循环节(循环节内部是后面的换到前面,最前面的换到最后面).线段树维护最大值,树状数组维护区间是否是循环节,查找前面最左边不是循环节的可用二分.我还是云里雾里的,看懂了网上的解题报告但还是不是完全明白题意:( 4 详细解释:http://blog.csdn.net/qq_24451605/article/details/47173933 5 */ 6 /******