Unique Palindromes(构造)

121 阅读2分钟

Problem - D - Codeforces

原题题面

image.png

题目描述

构造一个字符串 ss

对于该处的字符串给出 kk 个限制,每个限制给出一个 xx 和一个 cc,意为前 xx 个字符组成的子串有 cc 个回文子串。

共有 tt 组样例。

输入输出格式

image.png

输入样例

7
10 2
5 10
5 6
3 1
3
3
4 2
3 4
3 3
4 2
3 4
3 4
4 1
4
5
10 3
4 6 10
4 5 8
10 4
4 6 7 10
4 5 7 8

输出样例

YES
abcbbcabcb
YES
foo
YES
ayda
YES
wada
NO
YES
abcbcacbab
NO

题目分析

首先,我们可以证明对于“本质不同的回文子串”的数量,每个位置 ii 对于整体的数量最多只有 11 的贡献。

具体证明见 pzr的知乎题解

那么,对于每次向后的拓展,若 c[i]c[i1]>x[i]x[i1]c[i] - c[i - 1] > x[i] - x[i - 1],则意味着无法构造出符合要求的数列。

然后,我们注意到 kk 的限制不大于 2020,这是一个很重要的提示,先打个标记。

若后续拓展新得到的贡献为 xx,我们可以选择一个并未用过的字母,直接将 xx 个相应字母填到数列的后面,由于之前的证明,我们可以知道每个字母对总体的贡献有且仅为 11

而对于无贡献的拓展部分,我们可以以 abcabc 进行循环输出。这里需要注意两点,其一最开始的数列长度大于等于 33,我们可以一开始将初始数组定为 abcabc,然后按序进行拓展。其二,在两个相邻拓展的 abcabc 循环拆开拼合后必须连续,否则会出现 auuaauua 这种出现新贡献的错误。

具体解法见代码。

Accept代码

#include <bits/stdc++.h>

using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t; cin >> t;
    while (t --)
    {
        int n, k; cin >> n >> k;
        vector<int> x(k + 1), c(k + 1);
        bool flg = true;
        for (int i = 1; i <= k; i ++) cin >> x[i];
        for (int i = 1; i <= k; i ++) 
        {
            cin >> c[i];
            if (c[i] - c[i - 1] > x[i] - x[i - 1]) flg = false;
        }
        if (!flg)
        {
            cout << "NO" << "\n";
            continue;
        }
        int u = 3;
        cout << "YES\nabc";
        int m = 0;
        string sf = "abc";
        for (int i = 1; i <= k; i ++)
        {
            int xf = x[i] - x[i - 1];
            int cf = c[i] - c[i - 1];
            if (i == 1) xf -= 3, cf -= 3;
            for (int j = 0; j < cf; j ++) cout << (char)('a' + u);
            u ++;
            for (int j = cf; j < xf; j ++)
            {
                cout << sf[m];
                m = (m + 1) % 3;
            }
        }
        for (int i = x[k]; i < n; i ++) cout << 'a';
        cout << "\n";
    }
    return 0;
}