题解:P12127 [蓝桥杯 2024 省 B 第二场] 最强小队
P12127 [蓝桥杯 2024 省 B 第二场] 最强小队
首先题目要求要保持顺序,所以问题就是在序列内选两个数,贡献为他俩之间小于他俩的
计算小于某个数的数的数量,很明显是树状数组,但这道题权值的判断基准需要对左右端点取最小值否则无法确定,这样做必然要枚举端点,除非我们事先知道左右端点哪个大,只考虑以这个点为基准计算。
而且我们照着这样想就会发现,在保证右端点比左端点小的情况下,我们左端点会尽量往左取,毕竟多点数加进来比较绝对不是坏事。
于是现在要解决的问题就是,如何保证每次计算时左端点是大的那个,以及如何计算它能到的最右边的区间,以及如何计算这之间小于等于左端点的值的数的数量。
第一个和第二个比较好解决,就是先只枚举右端点,每次找到左边坐标最小且大于它的数,这个把
第二个问题可以考虑把这几个需要计算的区间记录到左端点上,即对每个
然后把序列倒过来再做一遍就行了。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
#define lowbit(x) x&(-x)
int a[MAXN],n,s[MAXN];
int c[MAXN],m,b[MAXN];
int mx[MAXN],ans,l[MAXN];
void add(int x)
{
for(int i=x;i<=m;i+=lowbit(i))
c[i]++;
}
int query(int x)
{
int sum=0;
for(int i=x;i;i-=lowbit(i))
sum+=c[i];
return sum;
}
vector<int>p[MAXN];
int solve()
{
for(int i=1;i<=n;i++)
mx[i]=max(mx[i-1],a[i]);
for(int i=1;i<=n;i++)
{
l[i]=lower_bound(mx+1,mx+1+i,a[i])-mx;
s[i]=query(a[i]-1);
add(a[i]);
if(l[i]!=i)p[l[i]].push_back(i);
}
memset(c,0,sizeof(c));
int ans=1;
for(int i=1;i<=n;i++)
{
for(auto now:p[i])
ans=max(ans,s[now]-query(a[now]-1)+2);
add(a[i]);
}
for(int i=1;i<=n;i++)
p[i].clear();
memset(c,0,sizeof(c));
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),
b[i]=a[i];
sort(b+1,b+1+n);
m=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+1+m,a[i])-b;
int ans=solve();
reverse(a+1,a+1+n);
printf("%d\n",max(ans,solve()));
return 0;
}