【MGVOI R1-B】完美重排 题解
FruitWasTaken · · 题解
出题人题解
主要知识点
- 【2】乘法原理
- 【3】递推法
- 【3】前缀和
- 【3】冒泡排序
Subtask 1:测试点 \bm{1\sim 6}
直接枚举 std::sort()
模拟即可,时间复杂度
Subtask 2:测试点 \bm{7\sim 12}
考虑优化 Subtask 1 的做法至
我们发现,假设刚计算完对区间
实际上没必要。由于我们只关心原先下标为
-
若
a_{R+1}<m ,则z_i 的值增加1 ; -
若
a_{R+1}>m ,则z_i 的值不变。
模拟一下发现是很好理解的,因为当
同理,也可以由区间
-
若
a_{L-1}>m ,则z_i 的值减小1 (将a_{L-1} 纳入重排区间后,m 被向前“推”了一个位置); -
若
a_{L-1}<m ,则z_i 的值不变。
综上,我们只需用双重循环枚举
Subtask3: 测试点 \bm{13\sim 20}
考虑将时间复杂度优化至
承 Subtask 2,我们只关心
-
情况 1:若
j<x_i 且a_j>m ,则b_j=-1 ; -
情况 2:若
j>x_i 且a_j<m ,则b_j=+1 ; -
其它情况,
b_j=0 。
数组
那么可以观察到:对区间
于是就等价于问你在值域为
一种做法是你可以算出
-
第一步,记录
x_i 之前所有-1 的位置和x_i 之后所有+1 的位置。显然,每两个相邻的-1 (或每两个相邻的+1 )之间会隔若干个0 。 -
第二步,根据上述分析,在我们选出的区间中,
+1 的个数要恰好比-1 的个数多y_i-x_i 。那么直接枚举-1 取多少个,这样+1 取多少个也就确定了。 -
第三步,确定了
+1 和-1 取多少个之后,只需再用乘法原理统计一下它们后面 / 前面的0 要怎么选。之前已经记录过所有-1 和+1 的位置,注意细节实现一下即可。
时间复杂度
注:考虑到这只是 T2,所以并没有卡常数优秀的
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, Q, a[15005];
int x, y;
int L[15005], R[15005];
void solve()
{
cin >> n >> Q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
while (Q--)
{
cin >> x >> y;
int index = x, cntL = 0, cntR = 0;
while (index <= n)
{
if (a[index] < a[x])
R[++cntR] = index;
++index;
}
index = x;
while (index >= 1)
{
if (a[index] > a[x])
L[++cntL] = index;
--index;
}
L[0] = R[0] = x;
L[cntL + 1] = 0, R[cntR + 1] = n + 1;
int ans = 0;
for (int i = 0; true; i++)
{
if (y - x + i > cntR || i > cntL)
break;
ans += (R[y - x + i + 1] - R[y - x + i]) * (L[i] - L[i + 1]);
}
cout << ans << '\n';
}
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _ = 1;
while (_--)
{
solve();
}
return 0;
}