2023年江苏省赛和湖南CCPC题解

2023 Jiangsu Collegiate Programming Contest, 2023 National Invitational of CCPC (Hunan), The 13th Xiangtan Collegiate Programming Contest

I. Elevator

题意:电梯中有n个人,m个人选择互不相同的楼层离开,问最多有多少人在同一层离开。

题解:输出 \(n-m+1\)

点击查看代码
void slove()
{
    int n, m; cin >> n >> m;
    cout << n - m + 1 << endl;
} 
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) slove();
    return 0;
}

J. Similarity (Easy Version)

题意:给定n个字符串,输出两个字符串的最长公共子串的长度。\(2\leq n\leq300,1\leq|s|\leq50\)

题解:枚举每两个字符串,求出最长公共子串即可。\(O(n^2 * |s|^2)\)

PS:子串是连续的,注意和子序列区分

点击查看代码
int LCS(string s1, string s2) {
    int n = s1.size(), m = s2.size();
    s1 = " " + s1;
    s2 = " " + s2;
    vector f(n + 5, vector<int>(m + 5));
    int res = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (s1[i] == s2[j]) f[i][j] = f[i - 1][j - 1] + 1;
            res = max(res, f[i][j]);
        }
    }
    return res;
}
void slove()
{
    int n; cin >> n;
    vector<string> a(n + 5);
    for (int i = 1; i <= n; i++) cin >> a[i];
    int res = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            res = max(res, LCS(a[i], a[j]));
        }
     }
     cout << res << endl;
} 
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) slove();
    return 0;
}

H. Neil's Machine

题意:给定两个长度为N的字符串,文本串S和目标串T,操作为:可以对文本串S的任意后缀将其偏移K位。\(1\leq K\leq25\)​。求最小的操作次数使得S和T匹配。\(1\leq N\leq2e5\)

题解:遍历S,统计偏移量,如果当前位置偏移的字符和T不同,选择累加操作1次,将其偏移为相同的字符,并累加偏移量。\(O(N)\)

点击查看代码
char cal(char c, int cnt) {
    int t = ((c - 'a' + cnt) % 26 + 26) % 26;  // 偏移量可能为负数
    char res =  (char)('a' + t);
    return res;
}
void slove()
{
    int n; cin >> n;
    string s, t; cin >> s >> t;
    int res = 0, sum = 0;
    for (int i = 0; i < n; i++) {
        char c = cal(t[i], sum);
        if (s[i] == c) continue;
        sum += s[i] - c;
        res++;
    }
    cout << res << endl;
} 

A. Today's Word

题意:给定一个长度为N(偶数)的字符串S,每次将这个字符串的前一半子串拼接在前面,再将后一半子串计算next(suf)拼接在后面(next操作就是对每个字符在字母表中往后偏移一次)。重复1e100次操作,求最后的字符串长度为M的后缀。\(1\leq N\leq1e5,1\leq M \leq1e5\)

题解:N需要为偶数,最小为2,S的后一半suf最小长度为1,每次扩展一倍。1e100次就是扩大 \(2^{1e100}\) 倍。suf拼接完成之后,最小长度也是大于M的,所以无需关系S的前一半子串。\(1e100\ mod \ 26 = 16\) ,对于suf可以计算出next 1e100 次之后的字符串。再往前面递推,直到递推出长度超过M即可停止。
最多往前递推几次呢?17次,\(2^{17}\) 就已经超过1e5长度了。

为什么得到最后的suf,在往前依次递推即可?上标表示next的次数

正推三次:\(suf->suf+suf^1->suf+suf^1+suf^1+suf^2->suf+suf^1+suf^1+suf^2+suf^1+suf^2+suf^2+suf^3\)

倒推三次:\(suf^3->suf^2+suf^3->suf^1+suf^2+suf^2+suf^3->suf+suf^1+suf^1+suf^2+suf^1+suf^2+suf^2+suf^3\)

倒退就是每次往前拼接偏移量为-1的子串。

点击查看代码
int cnt = 16;
char cal(char c) {
    int t = ((c - 'a' + cnt) % 26 + 26) % 26;
    char res = (char) t + 'a';
    return res;
}
string dfs(string s) {
    for (int i = 0; i < s.size(); i++) {
        s[i] = cal(s[i]);
    }
    return s;
}
void slove()
{
    int n, m; cin >> n >> m;
    string s; cin >> s;
    string suf = s.substr(n / 2, n / 2);
    suf = dfs(suf);
    cnt = -1;
    for (int i = 1; i <= 26; i++) {
        if (suf.size() >= m) break;
        suf = dfs(suf) + suf;
    }
    string res;
    res.resize(m);
    int idx = 0;
    for (int i = suf.size() - 1; idx < m; i--) {
        res[idx++] = suf[i];
    }
    for (int i = m - 1; i >= 0; i--) cout << res[i];
    cout << endl;
} 

K. Similarity (Hard Version)

题意:要求构造出N个互不相同的长度为K的字符串,要求最长公共子串的长度为M。如果不能构造输出No\(2\leq N\leq 300, 0\leq M\leq50,1\leq K\leq 100\)

题解:首先判断什么情况不能构造,当 \(K\leq M\) ,不能构造出互不相同的。当 \(M=0\) 时,\(N > 26\) 。要求N个字符串互相没有相同的部分,最多只能构造26个
如何构造:首先构造出相同的M长度,比如 \(M=3\)aaa 然后可以拼接不同的字符,达到互不相同的目的。aaabaaabaaaaaacaaacaaa
一个变量控制公共部分,一个变量控制不同。最多可以构造出 26+25+24+.......+1 = 351 个字符。

点击查看代码
void slove()
{
    int n, m, k; cin >> n >> m >> k;
    if (k <= m || (m == 0 && n > 26)) {
        cout << "No" << endl;
        return;
    }
    
    int x = 0; int y = 0;
    int cnt = 1;
    vector<string> res(n);
    for (int i = 0; i < n; i++) {
        string s; s.resize(k);
        int idx = 0;
        while (idx < k) {
            for (int j = 1; j <= m && idx < k; j++) {
                s[idx++] = 'a' + x;
            }
            if (idx < k) s[idx++] = 'a' + y;
        }
        res[i] = s;
        y++;
        if (y == 26) {
            x++;
            y = cnt;
            cnt++;
        }
    }
    cout << "Yes" << endl;
    for (int i = 0; i < n; i++) cout << res[i] << endl;
}

L. Architect

题意:有一个大的长方体,坐标从(0, 0, 0) 到 (W, H, L) 。现在有 N 个小长方体,判断这些小长方体是否可以将大长方体填满,并且不重叠。\(1\leq N\leq 3e5, 1\leq W,H,L\leq1e9\)

题解:三维差分,每次对于小长方体记录区域贡献+1,最后对整个区域-1,判断最后贡献是否全为0.

点击查看代码
int n, x, y, z;
struct node {
    int _1, _2, _3;
    bool operator < (const node& e2) const {
        if (_1 != e2._1) return _1 < e2._1;
        if (_2 != e2._2) return _2 < e2._2;
        return _3 < e2._3;  // 这里不能取等号
    }
};
map<node, int> d;
void cal(int x1, int y1, int z1, int x2, int y2, int z2, int c) {
    d[{x1, y1, z1}] += c;
    d[{x2 + 1, y1, z1}] -= c;
    d[{x1, y1, z2 + 1}] -= c;
    d[{x2 + 1, y1, z2 + 1}] += c;
    d[{x1, y2 + 1, z1}] -= c;
    d[{x2 + 1, y2 + 1, z1}] += c;
    d[{x1, y2 + 1, z2 + 1}] += c;
    d[{x2 + 1, y2 + 1, z2 + 1}] -= c;
}
void slove()
{
    cin >> x >> y >> z >> n;
    while (n--) {
        int x1, y1, z1, x2, y2, z2; cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
        cal(x1, y1, z1, x2 - 1, y2 - 1, z2 - 1, 1);
    }
    cal(0, 0, 0, x - 1, y - 1, z - 1, -1);
    bool flg = true;
    for (auto &[t, v] : d) {
        if (v != 0) {
            flg = false;
            break;
        }
    }
    if (flg) cout << "Yes" << endl;
    else cout << "No" << endl;
    d.clear();
} 
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) slove();
    return 0;
}
posted @ 2025-09-13 21:00  yairr  阅读(64)  评论(0)    收藏  举报