1. 程式人生 > 其它 >CF452F等差子序列 & 線段樹+hash查詢區間是否為迴文串

CF452F等差子序列 & 線段樹+hash查詢區間是否為迴文串

記錄一下一個新學的線段樹基礎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;
}