CF452F等差子序列 & 線段樹+hash查詢區間是否為迴文串
阿新 • • 發佈:2021-11-10
記錄一下一個新學的線段樹基礎trick(真就小學生trick唄)
給你一個1到n的排列,你需要判斷該排列內部是否存在一個3個元素的子序列(可以不連續),使得這個子序列是等差序列。n<=3e5
考慮等差數列的相關性質,對於一個3個數的等差數列,當 a[i] 作為中間項可行時,當且僅當一定存在至少1個 k,使得 a[i] - k 這個元素在它的左邊,a[i] + k 這個元素在它的右邊 (為了方便,這裡的k可以是負數)
那我們在順序列舉 a[i] 的過程中,不妨把用過的 a[i] 標記成1,沒用過的標記成零,然後對於所有範圍內的 k 暴力判一遍
於是你就得到了一個 n2 的暴力
大概長這樣
//Talking to the moon #include <bits/stdc++.h> #define N 1000010 #define M 2000010 #define int long long #define int_edge int to[M],val[M],nxt[M],head[N],cnt=0; using namespace std; int used[10010],a[10010]; //void add_edge(int x,int y ){to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;} int read(){ int fu=1,ret=0;char ch; for(;!isdigit(ch);ch=getchar())if(ch=='-')fu*=-1; for(;isdigit(ch);ch=getchar())ret=(ret<<1)+(ret<<3)+ch-'0'; return ret*fu; } signed main() { int n=read(),ans=0; for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++) { for(int j=1;a[i]-j>=1&&a[i]+j<=n;j++) if(used[a[i]-j]!=used[a[i]+j]){ans=1;break;} if(ans==1)break; used[a[i]]=1; } if(ans==1)puts("YES"); else puts("NO"); return 0; }
接下來是正解部分
我們發現正向考慮列舉 k 好像沒有什麼優化空間,而且題目也沒讓我們找 k ,那麼正難則反,我們可以考慮作為中間項不可行的情況,即不存在k使得 a[i] + k 和 a[i] - k 的標記相同
那不就是迴文串嗎
於是我們的問題就變成了在值域上搞單點修改加區間判斷是否迴文
先不考慮修改,判斷迴文串其實是有一個非常優秀的樸素Hash演算法的
就是考慮正反向各Hash一遍,然後判斷查詢區間的正反Hash值是否相等即可
然後Hash值是可以加減的
也就是說你把a在第1位的hash值加上b在第2位的hash值加起來就可以得到ab的hash值
那麼修改就簡單了,我們可以把這個Hash移到一棵線段樹上(樹狀陣列也可)
每個葉子節點存的是單點hash後的值
然後就可以支援單點修改,區間查詢了
需要注意的是合併的時候需要把每一位乘上這一位對應的hash底數(比如131的多少次方什麼的)
查詢的時候也是
//Talking to the moon
#include <bits/stdc++.h>
#define N 300010
#define M 2000010
#define int unsigned long long
#define int_edge int to[M],val[M],nxt[M],head[N],cnt=0;
using namespace std;
int a[N],h[N];
//void add_edge(int x,int y ){to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;}
int read(){
int fu=1,ret=0;char ch;
for(;!isdigit(ch);ch=getchar())if(ch=='-')fu*=-1;
for(;isdigit(ch);ch=getchar())ret=(ret<<1)+(ret<<3)+ch-'0';
return ret*fu;
}
int ls(int x){return x*2;}
int rs(int x){return x*2+1;}
struct Seg1{
int tr[N*4];
void update(int nw,int l,int r,int x){
if(l==r){
tr[nw]++;
return;
}
int mid=(l+r)/2;
if(x<=mid)update(ls(nw),l,mid,x);
else update(rs(nw),mid+1,r,x);
tr[nw]=tr[ls(nw)]*h[r-mid]+tr[rs(nw)];
}
int query(int nw,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tr[nw]*h[y-r];
}
int mid=(l+r)/2,sum=0;
if(x<=mid)sum+=query(ls(nw),l,mid,x,y);
if(y>mid)sum+=query(rs(nw),mid+1,r,x,y);
return sum;
}
}S1;
struct Seg2{
int tr[N*4];
void update(int nw,int l,int r,int x){
if(l==r){
tr[nw]++;
return;
}
int mid=(l+r)/2;
if(x<=mid)update(ls(nw),l,mid,x);
else update(rs(nw),mid+1,r,x);
tr[nw]=tr[rs(nw)]*h[mid-l+1]+tr[ls(nw)];
}
int query(int nw,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tr[nw]*h[l-x];
}
int mid=(l+r)/2,sum=0;
if(x<=mid)sum+=query(ls(nw),l,mid,x,y);
if(y>mid)sum+=query(rs(nw),mid+1,r,x,y);
return sum;
}
}S2;
signed main()
{
int n=read(),ans=0;
h[0]=1;for(int i=1;i<=n;i++)h[i]=h[i-1]*131;
for(int i=1;i<=n;i++)
{
a[i]=read();
int num=min(a[i]-1,n-a[i]),l=a[i]-num,r=a[i]+num;
if(S1.query(1,1,n,l,r)!=S2.query(1,1,n,l,r)){ans=1;break;}
S1.update(1,1,n,a[i]);S2.update(1,1,n,a[i]);
}
if(ans)puts("YES");
else puts("NO");
return 0;
}