링크 : https://ac.nowcoder.com/acm/contest/13504/B
출처 : Niuke.com , Niu
형제는 오랫동안 컴퓨터 앞에 앉아 일어 서서 바깥 쪽의 작은 언덕을보고 싶었습니다. 그래서 그는 다음과 같은 질문을했습니다.
크기가 n 인 배열 a가 주어지면 시퀀스 번호는 1부터 시작
하여 다음을 계산합니다.
max {RL | 1 <= L <= R <= n, a [L] == a [ R], 모든 i (L <= i <= R)에 대해 a [i]> = a [L]}을 충족합니다.
즉, 두 좌표를 찾으려면이 두 좌표의 값이 같고 값이 그들 사이는이 두 좌표보다 크거나 같음이 두 좌표
의 최대 뺄셈은 얼마입니까?
示例1
输入
复制
5
1 2 3 2 1
输出
复制
4
아이디어 : 선형 순회, 선분 트리를 사용하여 최소 간격 유지
AC 코드 :
#include<iostream>
#include<vector>
#include<map>
using namespace std;
const int maxn=1e6+10;
int arr[maxn];
int Min[maxn<<2];
map<int,int>fi; // 数字最小下标
vector<int>vec[maxn];
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
// ch 不是数字时
if (ch == '-') w = -1; // 判断是否为负
ch = getchar(); // 继续读入
}
while (ch >= '0' && ch <= '9') {
// ch 是数字时
x = x * 10 + (ch - '0'); // 将新读入的数字’加’在 x 的后面
// x 是 int 类型,char 类型的 ch 和 ’0’ 会被自动转为其对应的
// ASCII 码,相当于将 ch 转化为对应数字
// 此处也可以使用 (x<<3)+(x<<1) 的写法来代替 x*10
ch = getchar(); // 继续读入
}
return x * w; // 数字 * 正负号 = 实际数值
}
int min(int x,int y)
{
return (x<y)?x:y;
}
void PushUp(int rt)
{
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
void build(int l, int r, int rt)
{
if( l == r ){
Min[rt]=arr[l];
return ;
}
int mid =( l + r ) >> 1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
PushUp(rt);
}
int query(int i, int j, int l , int r, int rt)
{
if(i<=l && j>=r)
return Min[rt];
int mid=(l + r) >> 1;
int ans=0x3f3f3f3f;
if(i <= mid)
ans = min(ans,query(i ,j ,l ,mid ,rt<<1));
if(j > mid)
ans = min(ans,query(i ,j ,mid+1 ,r,rt<<1|1));
return ans;
}
int main()
{
int n;
// cin>>n;
n=read();
for(int i=1;i<=n;i++){
// cin>>arr[i];
arr[i]=read();
if(fi[arr[i]]==0){
fi[arr[i]]=i;
}
else{
vec[fi[arr[i]]].push_back(i); // fi[arr[i]]是将输入数字离散化 因为输入数字范围是1e9 ,使用数字首次出现下标来进行离散化
}
}
int ans=0;
build(1,n,1);//线段树区间最小值
map<int,int>::iterator iter;
for(iter=fi.begin();iter!=fi.end();iter++){
int num = (*iter).first;
// cout<<"num="<<num<<endl;
int l_pos = fi[num],sz=vec[fi[num]].size();
for (int i=0;i<sz;i++){
int r_pos = vec[fi[num]][i];
// cout<<"r_pos="<<r_pos<<" l_pos="<<l_pos<<endl;
if(query(l_pos,r_pos,1,n,1)>=num){
//区间最小值>=num
//更新答案
ans=max(ans,r_pos-l_pos); //坐标相减
}
else{
//说明[l,r]区间有小于num的数,则应该修改左端点l为当然r 这样遍历是线性的
l_pos = r_pos;
}
}
}
cout<<ans<<endl;
}