【解题报告】 CF 731D 80-th Level Archeology

$\text{Description}$

有n个数列,每个数列长度可能不一样,同时有一个c,你有一种操作,让这n个数列中所有小于c的数都加1,所有等于c的数变成0.问你最少可以操作几次可以让这n个数列满足字典序.

$\text{Solution}$

我们可以发现,对于任意两个相邻的数列,操作数k有一个符合的区间,且k一定小于c,因为进行c次操作之后的数列与原数列相同,相当于有一个循环的过程。只要操作数k在这个区间里面,上面的数列字典序总小于下面的数列。说得通俗一点,有n-1个区间(有特殊情况,看代码),要求这n-1个区间的交。
怎么做这个问题呢,我们可以采取差分+前缀和的方式进行处理,具体方法就是区间两端位置放个++和—,统计时从前到后加起来就可以了。
下面来讨论一下每种情况:
首先假设有两个word,分别叫做a和b,并且我们定义两个word的失配位置为两个word从前往后逐位比较,所找到的第一个有不相同字符的位置。
具体情况有一下四种:
1.失配位置在a,b中(表示没有一个word是另一个word的前缀),且失配位置上的数a比b小。
举个栗子 a=13 b=15 c=7明显地,失配位置在第二位,失配位置上的数满足a比b小。画个表格模拟一下:

操作次数 a b
0 13 15
1 24 26
2 35 37
3 46 40
4 57 51
5 60 62
6 71 73

可以看出,失配位置前的数字对结果不产生影响(因为始终相同,无法比较字典序),由表格易知当操作数在$[0, 2] \cup [5, 6]$的情况下是可行的。这个情况比较复杂,因为举其他例子时有可能第二个区间退化成一个数甚至消失,这会不会对我们的程序产生什么影响呢(比如说要特判之类的)?答案是不会。因为假设第二个区间的形式是$[p, q]$,第二个区间消失时一定满足$p = q + 1$(这个很重要,要自己多举几个例子理解一下,我这里就不证明了),而我们的程序会在$p$的位置+1,在$q + 1$的位置-1,两个加一减一相互抵消了,因此对我们的程序不产生影响。
2.失配位置在a,b中(表示没有一个word是另一个word的前缀),且失配位置上的数a比b大。
仍然举个栗子 a=14 b=12 c=7,这个相对较易,我就不画表格模拟了,操作数的区间为$[4, 5]$
3.失配位置在a的末尾的后一个位置(a是b的前缀),这个很简单不管你怎么操作a始终比b小,操作区间$[0, c - 1]$
4.失配位置在b的末尾的后一个位置(b是a的前缀),这个也很简单,直接就是无解,不需要进行任何操作,直接输出无解可以,continue也可以(缺少一个区间就永远无法让k满足n-1个区间)。
四种情况讨论完了,具体详情看代码吧。

$\text{Code}$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN = 1e6 + 10;
const int MAXC = 1e7 + 10;
vector<int> words[MAXN];
int cnt[MAXC], n, c;
void calc(int a, int b)
{
int index = 0;
while (index < words[a].size() && index < words[b].size()) //失配位置查询
{
if (words[a][index] != words[b][index]) break;
index++;
}
if (index < words[a].size() && index < words[b].size()) // 失配位置在a, b中
{
if (words[a][index] < words[b][index]) // 失配位置a小于b
{
cnt[0]++;
cnt[c - words[b][index] + 1]--;
cnt[c - words[a][index] + 1]++;
cnt[c]--;
}
else
{
cnt[c - words[a][index] + 1]++;
cnt[c - words[b][index] + 1]--;
}
}
else if (index == words[a].size() && index != words[b].size()) //a比b短
{
cnt[0]++;
cnt[c]--;
}
else if (index != words[a].size() && index == words[b].size()); // a比b长,无解
else //a和b一样长
{
cnt[0]++;
cnt[c]--;
}
}
int main()
{
scanf("%d%d", &n, &c);
for (int i = 1;i <= n;i++)
{
int len, w;
scanf("%d", &len);
while (len--)
{
scanf("%d", &w);
words[i].push_back(w);
}
}
for (int i = 1;i <= n - 1;i++) calc(i, i + 1);
bool flag = false;
int sum = 0;
for (int i = 0;i < c;i++)
{
sum += cnt[i];
if (sum == n - 1)
{
flag = true;
printf("%d\n", i);
return 0;
}
}
if (!flag) printf("-1\n");
return 0;
}