一類區間查詢對應答案具有單調性--維護字首對應端點最大值

potential-star發表於2024-03-30

https://www.luogu.com.cn/problem/P8773

[藍橋杯 2022 省 A] 選數異或

題目描述

給定一個長度為 \(n\) 的數列 \(A_{1}, A_{2}, \cdots, A_{n}\) 和一個非負整數 \(x\), 給定 \(m\) 次查詢, 每次詢問能否從某個區間 \([l, r]\) 中選擇兩個數使得他們的異或等於 \(x\)

Solution:我們預處理每個端點對應的左邊最近的配點,然後\(O(1)\)回答詢問。

詳細來說,對於每個右端點我們找到其左邊最近的對應位置。進一步,我們考慮對於一個區間,我們只需要存在一個點他的對應端點在區間內就可以了,於是我們需要維護對應端點的區間最大值,RMQ問題可以線段樹或者st表解決。

但我們可以利用我們只需要回答存在性,而不用具體回答,我們只需要維護字首最大值,對於每個查詢只需要檢驗ans[r]>=l即可

int a[N];
int ans[N];
map<int,int>mp;
//ans[i]記錄字首i中能夠異或後成x的位置的最大值
void solve(){
	cin>>n>>m;
	int x;cin>>x;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		int tmp=a[i]^x;
		if(mp.count(tmp)){
			ans[i]=mp[tmp];
			ans[i]=max(ans[i-1],ans[i]);
		}
		else {
		mp[tmp]=0;
		ans[i]=ans[i-1];
		}
		mp[a[i]]=i;
	}
	for(int i=1;i<=m;i++){
		int l,r;cin>>l>>r;
		if(ans[r]>=l)cout<<"yes"<<endl;
		else cout<<"no"<<endl;
	}
}

牛課周賽38F

連結:https://ac.nowcoder.com/acm/contest/78292/F
來源:牛客網

小苯有一個長度為 \(n\) 的陣列 \(a\),他定義一個陣列是好陣列,當且僅當該陣列是一個迴文陣列,且長度嚴格大於 \(2\)

他現在進行了 \(q\) 次詢問,每次詢問都給出一段區間 \([l, r]\),他想知道 \(a\) 在這一段區間中是否存在一個子序列是一個好陣列,請你幫幫他吧。
輸入描述:

Solution:首先我們注意子序列,然後會發現任何一個長度迴文子序列都規約到一個長度為3的迴文子序列,所以我們只需要找到對於當前右端點,我們在i-1裡去尋找對應的最大位置j滿足a[j]==a[i].

對於一個區間來說,我們只需要存在i是的對應的j在區間內,也就是大於L,所以依然是維護區間對應數的最大值。值得注意區別上題的是,我們對於i是需要在i-1的字首最大值裡找答案,因為要求長度為3,所以要有一個空隙。

int n, m;
int a[N];
//我們先找對於每個右端點最近的和他相等的數,當然由於要構造長度
//大於2的迴文,所以我們要營造時間差,我們只能在1-i-2開始考慮

int ans[N];
map<int,int>mp;
//我們維護上述這個性質的字首最大值
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		if(mp.count(a[i])){
			ans[i]=max(ans[i-1],mp[a[i]]);
		}
		else {
			ans[i]=ans[i-1];
		}
		mp[a[i-1]]=i-1;
	}
	for(int i=1;i<=m;i++){
		int l,r;cin>>l>>r;
		if(ans[r]>=l)cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
}

廣州大學2024校賽E題

連結:https://ac.nowcoder.com/acm/contest/77448/E
來源:牛客網

\(n\) 顆星星都會發出各種顏色的光,其中第 \(i\) 個星星發出的光的編號為 \(col_i\)
小A很無聊,於是以第一顆星星為根,用 \(n\) 顆星星建立了一棵樹;
一顆星星是 漂亮的 當且僅當以這顆星星為根的子樹的所有星星顏色都不同;
到底有多少星星是漂亮的呢?

前言:本題最大N=2e6,\(O(nlogn)\)做法需要非常小的常數,鏈式前向星登場(bushi。

Solution:首先利用dfs序將樹上問題轉化到序列上。原問題等價於每次查詢區間[l,r](子樹dfs序連續)是不是每個數都不同,我們應該考慮預處理pre[i]表示i上次出現的最近位置,然後\(O(1)\)查詢。同樣套路的,我們只需要維護字首最大值就可以了

int n, m;
int a[N];
vector<int>e[N];
int l[N],r[N];
int ans[N];
int tong[N];
int idx=0;
int dfn[N];
//所以dfs序標準寫法在哪?
void dfs(int u,int fa){
	dfn[++idx]=u;
	l[u]=idx;
	for(auto v:e[u]){
		if(u==fa)continue;
		dfs(v,u);
	}
	r[u]=idx;
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n-1;i++){
		int x;cin>>x;
		e[x].push_back(i+1);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		int u=dfn[i];
		int col=a[u];
		if(tong[col]){
			ans[i]=max(ans[i-1],tong[col]);
		}
		else {
			ans[i]=ans[i-1];
		}
		tong[col]=i;
	}
	int cnt=0;
	
	//for(int i=1;i<=n;i++)cerr<<dfn[i]<<" ";
	//cerr<<endl;
	for(int i=1;i<=n;i++){
		int u=dfn[i];
		int ql=l[u];
		int qr=r[u];
		//cerr<<u<<" "<<ql<<" "<<qr<<endl;
		if(ans[qr]>=ql)cnt++;
		
	}
	cout<<n-cnt<<endl;
}

出題人標程:dfs過程中計算答案

#include <bits/stdc++.h>
using namespace std;
int n,dfn,ans,mx;
vector<int> a,id;
vector<vector<int>> e;
void dfs(int u){
int l=++dfn;
mx=max(mx,id[a[u]]);
id[a[u]]=dfn;
for(int v:e[u])dfs(v);
int r=dfn;
if(mx<l)++ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;cin>>n;
a.resize(n+1);
id.resize(n+1);
e.resize(n+1);
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=2;i<=n;++i){
int x;cin>>x;
e[x].push_back(i);
}
dfs(1);
cout<<ans<<"\n";
return 0;
}

相關文章