题解:P12446 [COTS 2025] 答好位 / Vrsta
挑战最短代码。
显然,原问题等价于求出每个区间的次大值下标。于是我们可以打表观察区间次大值的结构。
例如,当
0 1 1 4 2 2 2 2 2
0 0 3 4 2 2 2 2 2
0 0 0 3 4 6 6 8 8
0 0 0 0 4 6 6 8 8
0 0 0 0 0 6 6 8 8
0 0 0 0 0 0 7 6 6
0 0 0 0 0 0 0 7 9
0 0 0 0 0 0 0 0 9
0 0 0 0 0 0 0 0 0
观察发现,每个元素最多占据两个矩形区域。下文中用
证明:
考虑一个元素何时成为区间次大值。
设
pre_i 表示i 前面最后一个比a_i 大的位置,prepre_i 表示i 前面倒数第二个比a_i 大的位置;nxt_i 表示i 后面最前一个比a_i 大的位置,nxtnxt_i 表示i 后面第二个比a_i 大的位置。则
i 成为次大值的区域为:[prepre_i+1,pre_i]\times [i,nxt_i-1]\cup [pre_i+1,i]\times [nxt_i,nxtnxt_i-1]
于是我们定位到每个矩形即可。
考虑递归进行这个过程。如果我们知道
此时我们可以提取出矩形
递归时进行记忆化,则每个矩形恰好被递归一次,总递归次数为
除第一次外区间的最大值下标可以在递归时传下去。第一次使用
code:
#include<bits/stdc++.h>
#define rep(i,j,k,...) for(int i=j;i<=k;i++)
using namespace std;
const int maxn=2070;
int a[maxn],n;
int ans[maxn][maxn];
int ask(int l,int r){
cout<<"? "<<l<<" "<<r<<endl;
int x;
cin>>x;
return x;
}
bool vis[maxn][maxn];
void solve(int l,int r,int mx){
if(l==r) return;
if(vis[l][r]) return;
vis[l][r]=1;
int se=ask(l,r),x,y;
tie(x,y)=minmax(mx,se);
rep(i,l,x) rep(j,y,r) ans[i][j]=se;
solve(l,y-1,x),solve(x+1,r,y);
}
signed main(){
cin>>n;
int x=ask(1,n),l=x,r=x;
while(r!=n&&ask(l,r+1)!=x) r++;
while(l!=1&&ask(l-1,r)!=x) l--;
int pos=(l==1? r+1: l-1);
solve(1,n,pos);
cout<<"!"<<endl;
int q;
cin>>q;
while(q--){
int l,r;
cin>>l>>r;
cout<<ans[l][r]<<endl;
}
}