【题解】Codeforces731D 80-th Level Archeology

传送门

题目解释:

有n个数列,每个数列长度可能不一样,同时有一个c,你有一种操作,让这n个数列中所有小于c的数都加1,所有等于c的数变成0.问你最少可以操作几次可以让这n个数列满足字典序
我们可以发现,对于任意两个相邻的数列,操作数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小。画个表格模拟一下:

操作次数ab
01315
12426
23537
34640
45751
56062
67173

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

Codes

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值