2018.11.02【校內模擬】優美的序列(單調棧)

傳送門


解析:

顯然我們需要處理出所有數能夠管轄的區間的範圍。也就是處理出左右端點。

整除?這個還是可以用單調棧維護的,先講做法,再證明。

維護棧中每個數在數列中的位置,遇到下個數先判斷它是否是棧頂數的倍數,否則一直彈棧頂並判斷,直到棧爲空。這一輪中彈出去的數能夠管轄的邊界就在這個數的前一個位置。

考慮最開始棧時空的,所以這個策略得到的棧任意時刻上面的數都是下面的數的倍數。
而一個數倍數的倍數肯定還是它的倍數,所以不可能出現越出實際管轄範圍的情況。
而只要是倍數就不會造成彈棧,所以不可能出現在管轄邊界之前就彈出去的情況。

綜上,單調棧的正確性是毋庸置疑的。

複雜度也因爲單調棧而優化到了優秀的O(n)O(n)


代碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=500005;
int sta[N],a[N];
int lbound[N],rbound[N];
int n,top;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i){
		a[i]=getint();
	}
	
	for(int re i=1;i<=n;++i){
		while(top&&a[i]%a[sta[top]])rbound[sta[top--]]=i-1;
		sta[++top]=i;
	}
	while(top)rbound[sta[top--]]=n;
	
	for(int re i=n;i;--i){
		while(top&&a[i]%a[sta[top]])lbound[sta[top--]]=i+1;
		sta[++top]=i;
	}
	while(top)lbound[sta[top--]]=1;
	
	vector<int> ans;
	int maxn=0;
	for(int re i=1;i<=n;++i){
		if(rbound[i]-lbound[i]>maxn){
			maxn=rbound[i]-lbound[i];
			ans.clear();
		}
		if(rbound[i]-lbound[i]==maxn){
			ans.push_back(lbound[i]);
		}
	}
	sort(ans.begin(),ans.end());
	ans.erase(unique(ans.begin(),ans.end()),ans.end());
	printf("%d %d\n",ans.size(),maxn);
	for(vector<int>::iterator it=ans.begin();it!=ans.end();++it)printf("%d ",*it);
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章