Skip to content
26牛客暑假多校4

26牛客暑假多校4

点击查看题面

I题 Rounddog II

题目大意

给定一个字符串 S 和一个整数 k。定义 Tk 为在字符串 "Rounddo" 后接 k 个字符 'g' 组成的模式串,即:

Tk="Rounddo"+"g""g""g"k 个

考虑 S 的所有循环移位(Cyclic Shift)。字符串 S 的长度为 |S|,共有 |S| 个不同的循环移位。 任务是统计在 S 的所有循环移位中,有多少个包含 Tk 作为连续子串。

数据范围:

  • 测试用例数:1t105
  • 字符串长度:1|S|105,所有测试用例的 |S| 之和满足 |S|105
  • 整数 k1k100
  • 字符集:S 仅包含大写和小写英文字母

思路

本题的核心在于破环成链以及对匹配位置的组合计数与边界分析

  1. 目标串构造: 根据题意构造目标字符串 Tk="Rounddo"+string(k,’g’),其长度为 L=|Tk|=7+k
  2. 边界剪枝: 若原串长度 |S|<L,则 S 的任何循环移位都不可能包含长度更大的 Tk,直接输出 0
  3. 破环成链与匹配计数: 将字符串 S 复制一份拼接在其后,形成长度为 2|S| 的扩展字符串 S=S+S。 遍历 S 前半部分(即起始下标在 [0,|S|1] 范围内)中所有 Tk 的出现位置,统计匹配次数 cnt
  4. 分类讨论与数学推导
  • 情况 1 (cnt=0): 说明原串及其循环拼接中完全不包含 Tk,因此包含 Tk 的循环移位数量为 0
  • 情况 2 (cnt=1): 假设 TkS 中仅在下标 pos (0pos<|S|) 处匹配成功一次。 每一个循环移位对应 S 中一个长度为 |S| 的滑动窗口 [i,i+|S|1] (0i<|S|)。 该窗口能完整包含 Tk 的合法起始点 i 分为两类:
  • 覆盖第一个 Tk(区间 [pos,pos+L1]):需要 iposi+|S|1pos+L1ipos+L|S|。由于 L|S|,此条件下仅有 i=pos 这一种合法情况。
  • 覆盖第二个 Tk(区间 [pos+|S|,pos+|S|+L1]):需要 i+|S|1pos+|S|+L1ipos+L
  • 综上,合法起始点 i 的总数为:
1+(|S|(pos+L))=|S|L+1

此公式结果与具体的匹配位置 pos 无关。

  • 情况 3 (cnt2): 当 TkS 中出现至少两次时,两次匹配覆盖的合法移位区间相互重叠并无缝衔接,覆盖了所有 |S| 个可能的循环移位起始点,因此合法移位数量为 |S|

复杂度分析

时间复杂度: 对于每个测试用例,构造模式串耗时 O(L)。字符串匹配查找可借助 std::string::find 完成,平均耗时为 O(|S|)。整体时间复杂度为 O(|S|),满足算法时限要求。

空间复杂度: 需要存储拼接串 S=S+S 以及模式串 Tk,整体辅助空间复杂度为 O(|S|+k)

参考代码

参考代码
c++
#include <iostream>
#include <string>

using namespace std;
using ll = long long;

void fc() {
    string s;
    int k;
    cin >> s >> k;
    
    string ch = "Rounddo" + string(k, 'g');
    
    if (s.size() < ch.size()) {
        cout << 0 << "\n";
        return;
    }
    
    int cnt = 0;
    size_t n = s.size();
    
    s += s;
    
    for (size_t pos = s.find(ch); pos != string::npos && pos < n; pos = s.find(ch, pos + 1)) {
        cnt++;
    }
    
    if (!cnt) {
        cout << 0 << "\n";
    } else {
        cout << (cnt == 1 ? n - ch.size() + 1 : n) << "\n";
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t = 1;
    cin >> t;
    while (t--) {
        fc();
    }
    return 0;
}

B题 Quadratic Residue

题目大意

给定一个正整数 p,求一组正整数解 (x1,x2,q),使得满足以下条件:

  1. 1x1<q1x2<p
  2. x12p(modq)
  3. x22q(modp)
  4. 1q1012

若存在多组解,输出任意一组合法整数;若不存在解,则输出 Impossible

数据范围:

  • 测试用例数:1T104
  • 正整数 p2p109

思路

本题是一道巧用代数构造解决模同余问题的数论题。

  1. 同余方程组的代数化简: 考虑第一个同余条件 x12p(modq),这意味着 x12pq 的整数倍。 最直接的构造方式是取 1 倍关系,即令:
q=x12p

此时,条件 x12p(modq) 显然成立。 2. 第二个同余条件的简化: 将 q=x12p 代入第二个同余式 x22q(modp),可得:

qx12px12(modp)

由此可知,只需令 x2=x1,同余式 x22q(modp) 便自然成立。 3. 求解变量边界与取值: 根据题目约束:

  • 需要满足 x1<qx1<x12px12x1p>0。 解一元二次方程 x12x1p=0 的正根为:
x1=1+1+4p2

为了保证整数且严格满足不等式,可取:

x1=1+1+4p2+1
  • p4 时,由于 x1p+1,必然满足 x2=x1<p
  • 同时,q=x12p2p+1,当 p109 时,q1012,完全满足范围限制。
  1. 小数据特判: 对于 p=2p=3 的边界情况,可以通过直接手算构造出合法解:
  • p=2:取 (x1,x2,q)=(12,1,71)
  • p=3:取 (x1,x2,q)=(4,2,13)

复杂度分析

时间复杂度: 对于每个测试用例,计算 x1q 仅涉及常数次算术运算与一次开方操作,单次复杂度为 O(1)。处理 T 个测试用例的总时间复杂度为 O(T)

空间复杂度: 仅使用常数个辅助变量,空间复杂度为 O(1)

参考代码

参考代码
c++
#include <iostream>
#include <cmath>

using namespace std;
using ll = long long;

void fc() {
    ll p; 
    cin >> p;

    if (p == 2) {
        cout << "12 1 71\n";
        return;
    }
    
    if (p == 3) {
        cout << "4 2 13\n";
        return;
    }

    ll x1 = (1 + (ll)sqrt(1 + 4 * p)) / 2 + 1;
    ll q = x1 * x1 - p;

    cout << x1 << ' ' << x1 << ' ' << q << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    cin >> t;
    while (t--) {
        fc();
    }
    return 0;
}