codeforce刷题(二)
CodeForces 1329A Dreamoon Likes Coloring
题意
\(n\)个格子,\(m\)次操作,第\(i\)次操作使用第\(i\)种颜色连续的染\(l_i\)个格子,求给出任意一种方案,使得:
- 每个格子都被染上色
- 每种颜色都要出现,既不能存在一种颜色被完全覆盖
题解
显然,如果 \(\sum_{i = 1}^{m}l_i < n\),则不存在这样的方案。
假如第\(i\)种颜色就从第\(i\)个格子开始染色,有两种情况可能出现
- 如果有 \(i + l[i] - 1 > n\),则不存在满足条件的方案,因为要染第\(i\)种颜色,且不超出\(n\)个格子的方案,只有覆盖掉\(i\)前面的颜色,所以不满足条件2:不会有颜色被完全覆盖
- 没有 \(i + l[i] - 1 > n\),说明\(m\)次操作没有染完全部的格子或者刚好染完全部的格子。从后向前考虑,第\(m\)次操作在第 \(n - l[m] + 1\) 格子处开始染色, 第\(m - 1\)次操作在第 \(n - l[m] - l[m - 1] + 1\) 格子处开始染色,第\(m - 2\)次操作在第 \(n - l[m] - l[m - 1] - l[m - 2] + 1\) 格子处开始染色,依次递推。在递推的过程中,判断上一种颜色染的区域的末尾是不是超过了当前颜色的染色开始位置。举例来说:如果 \((m - 1) + l[m - 1] >= n - l[m] + 1\),说明所有的格子都被染色,且每种颜色都会出现。
注意是按顺序操作 ,所以不能排序。同时,这也是为什么出现 \(i + l[i] - 1 > n\) 就代表着\(i\)前面的颜色会被完全覆盖。非常妙的思想:先紧凑染色,然后从后向前考虑,将染色的区域向后移动,起到补偿的作用。动画演示
int main()
{
cin >> n >> m;
long long sum = 0;
for (int i = 1; i <= m; ++i) {
cin >> l[i];
sum += l[i];
ans[i] = i;
if (i + l[i] - 1 > n) {
puts("-1");
return 0;
}
}
if (sum < n) {
puts("-1");
return 0;
}
for (int i = m; i >= 1; --i) {
ans[i] = n - l[i] + 1;
n = n - l[i];
if (i - 1 + l[i - 1] >= ans[i]) {
break;
}
}
for (int i = 1; i <= m; ++i) cout << ans[i] << " ";
cout << endl;
return 0;
}
CodeForces 1326D2 Prefix-Suffix Palindrome (Hard version)
给一个字符串\(s\),求满足如下条件的最长回文串\(t\):
- \(t = a + b\)
- \(a\)是\(s\)的前缀(\(a\)可以为空),\(b\)是\(s\)的后缀(\(b\)可以为空)
题解
分类讨论,
- \(t = a\),表示\(t\)是\(s\)的前缀且是回文串,马拉车算法求得\(p[i]\)后,判断\(i - p[i] == 0\),更新答案
- \(t = b\),表示\(t\)是\(s\)的后缀且是回文串,判断\(i + p[i] == m\),更新答案
- \(t = a + b\),存在某个\(k\),使得\(s[1] = s[n],s[2] = s[n - 1], ··· , s[k] = s[n - k + 1], s[k + 1] != s[n - k]\),掐头(去掉\(s[1 - k]\))去尾(去掉\(s[n - k + 1 - k]\))以后,就变成了上述两种情况。用相同的方法解决即可
换个问题,如果\(t\)是回文子串,既连续不断,它仍然可以分成两部分,一部分是\(s\)的前缀,一部分是\(s\)的后缀。然后求\(t\)该怎么做?
void Mana(string s, int n) {
int m = 1;
vector<char> ma(3 * n);
vector<int> p(3 * n);
// inite string s
ma[0] = '@';
for (int i = 0; i < n; ++i) {
ma[m++] = '#';
ma[m++] = s[i];
}
ma[m] = '#';
//printf("%s\n", ma);
int id = 0, mx = 0, ans = 0, pos = 0;
for (int i = 0; i < m; ++i) {
if (i < mx) p[i] = min(mx - i + 1, p[2 * id - i]);
else p[i] = 1;
while(ma[i - p[i]] == ma[i + p[i]]) p[i]++;
if (i + p[i] - 1 > mx) {
id = i;
mx = i + p[i] - 1;
}
if ((i - p[i] == 0 || i + p[i] == m + 1) && (p[i] - 1 >= ans)) {
ans = p[i] - 1;
pos = i;
}
}
myfor(i, 0, m) if (pos - p[pos] < i && i < pos + p[pos] && ma[i] != '#') printf("%c", ma[i]);
}
void solve() {
string s;
cin >> s;
int k = 0, n = s.size();
while(s[k] == s[n - k - 1] && (2 * k + 1 < n)) k++;
if (k > 0) cout << s.substr(0, k);
if (n > 2 * k) {
Mana(s.substr(k, n - 2 * k), n - 2 * k);
}
if (k > 0) cout << s.substr(n - k, k);
cout << endl;
}