傳送門
解析:
顯然我們需要處理出所有數能夠管轄的區間的範圍。也就是處理出左右端點。
整除?這個還是可以用單調棧維護的,先講做法,再證明。
維護棧中每個數在數列中的位置,遇到下個數先判斷它是否是棧頂數的倍數,否則一直彈棧頂並判斷,直到棧爲空。這一輪中彈出去的數能夠管轄的邊界就在這個數的前一個位置。
考慮最開始棧時空的,所以這個策略得到的棧任意時刻上面的數都是下面的數的倍數。
而一個數倍數的倍數肯定還是它的倍數,所以不可能出現越出實際管轄範圍的情況。
而只要是倍數就不會造成彈棧,所以不可能出現在管轄邊界之前就彈出去的情況。
綜上,單調棧的正確性是毋庸置疑的。
複雜度也因爲單調棧而優化到了優秀的。
代碼:
#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;
}