P10995 【MX-J3-T2】Substring
也是在赛时做出黄题了(后话:现在降 橙 \color{#f39c11}\texttt{橙} 橙 力,后话的后话:现在又升 黄 \color{#ffc627}\texttt{黄} 黄 了)。
首先考虑暴力求出所有字串,排序一遍,时间复杂度 O ( n 2 ) O(n^2) O(n2) ,预期得分 45 p t s 45pts 45pts 。
思考如何不求出所有字串就可以查询出第 k k k 个字串。以样例为例。
3 6
3 1 2
1
2
3
4
5
6
答案:
2 2
2 3
3 3
1 1
1 2
1 3
观察到数字 1 1 1 开头的字串一定大于其他数字开头的字串(证明:根据字典序定义显然),而根据序列性质: 其中 1∼n 各出现了一次 ,数字 1 1 1 开头的子串越长排名越大。具体地 [ 1 ] < [ 1 , 2 ] [1] < [1,2] [1]<[1,2] ,于是根据数字在原数组中的位置,可以求出以数字 x x x 开头的子串数量,并且它们的排名也因此确定了。
解释:令
S
x
S_x
Sx 代表以
x
x
x 开头的子串的集合,那么显然有
S
1
<
S
2
<
S
3
<
.
.
.
<
S
n
S_1 < S_2 <S_3<...<S_n
S1<S2<S3<...<Sn (这里比较每个集合内子串字典序大小)。
并且每个
S
x
S_x
Sx 内的排名也是确定且与子串大小相关(如以
3
3
3 开头,长度为
2
2
2 的子串的排名就是以
3
3
3 开头长度为
1
1
1 的字串的排名
+
1
+1
+1,注意这里以
x
x
x 开头长度为
1
1
1 的字串排名是可以预处理出来的)。
接下来看具体实现:
p o s i pos_i posi 表示第数字 i i i 在原数组出现位置。
l e n i len_i leni 表示以数字 i i i 开头的子串长度。
p r e pre pre 是 l e n len len 的前缀和, p r e i pre_i prei 也就表示以 i i i 开头,长度为 1 1 1 的子串的排名。
于是对于每个查询 k k k ,二分求出其子串开头是什么,然后整个区间根据已知信息,手推一遍样例也就出来了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#define int long long
using namespace std;
const int maxn = 3e5 + 5;
int n, q, k;
int a[maxn];
map<int, int> pos;
int len[maxn], pre[maxn];
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
pos[a[i]] = i;
}
for (int i = 1, num; i <= n; i++)
{
num = pos[i];
len[i] = n - num + 1;
}
// for (int i = 1; i <= n; i++) {
// cout << len[i];
// }
// cout << endl;
for (int i = 2; i <= n + 1; i++)
{
pre[i] = pre[i - 1] + len[i - 1];
}
// for (int i = 1; i <= n + 1; i++) {
// cout << pre[i] << " ";
// }
// cout << endl;
while (q--)
{
scanf("%lld", &k);
// cout << k << " ";
int x = lower_bound(pre + 1, pre + n + 2, k) - pre - 1;
// cout << x << "# ";
if (x == n + 1)
{
cout << pos[n] << " " << pos[n] + len[n] - 1 << endl;
}
else
{
if (k == pre[x])
{
cout << pos[x] << " " << pos[x] + k - pre[x] << endl;
}
else
{
cout << pos[x] << " " << pos[x] + k - pre[x] - 1 << endl;
}
}
}
return 0;
}