P9087 「SvR-2」音符
P9087 「SvR-2」音符
题目描述
本题中「子串」指:
若字符串 \(s\) 中有一段连续的字符构成字符串 \(p\),则 \(p\) 是 \(s\) 的子串。
我们用一个字符串代替一份乐谱,用字符代替每一个音符。
我们定义「重音」表示乐谱中出现了两个连续的相同字符,如 \(\tt eeeee\) 中存在 \(4\) 个「重音」。
现在 Sept 准备写一份长度为 \(n\) 的乐谱给 Tpes 看,他对乐谱的评价标准如下:
- 乐谱中每出现一个「重音」,他的愤怒值就会增加 \(a\)。
- 乐谱中每有一段长度为 \(k\) 的子串中不存在「重音」,他的愤怒值就会增加 \(b\)。
现在已知 \(n,k,a,b\),请你帮 Sept 构造出一份乐谱,使得 Tpes 的愤怒值 \(x\) 最小。
输入格式
本题有多组数据。
第一行一个整数 \(T\),表示数据组数。
接下来 \(T\) 行,每行两个整数 \(n,k,a,b\),意义如题目所述。
输出格式
共 \(2 \cdot T\) 行,对于每组数据都输出两行:
- 第 1 行表示 Tpes 最小的愤怒值 \(x\)。
- 第 2 行表示你构造出的乐谱。
输入输出样例 #1
输入 #1
2
4 5 2 2
8 6 3 2
输出 #1
0
Sept
3
2023yyds
说明/提示
数据规模与约定
本题采用捆绑测试。
| \(\bf{Subtask}\) | \(\bm{n\le}\) | \(\bm{\sum n\le}\) | \(\bm{T\le}\) | \(\bf{Score}\) |
|---|---|---|---|---|
| \(\sf 1\) | \(6\) | \(10\) | \(3\) | \(\tt 10\) |
| \(\sf 2\) | \(10^3\) | \(2\times 10^3\) | 无特殊限制 | \(\tt 30\) |
| \(\sf 3\) | 无特殊限制 | 无特殊限制 | 无特殊限制 | \(\tt 60\) |
对于 \(100\%\) 的数据,有 \(2\le T\le 100\),\(2\le n,k\le 10^5\),\(1\le a,b\le 10^9\)。单组数据内保证 \(\sum n\le 2\times 10^5\)。
输出注意事项
输出 \(x\) 和构造乐谱可以看作是两个子问题,如果你只会完成其中的一个,请在另一个子问题对应的地方用符合要求的字符或数字占位。
乐谱中你可以输出任意字符,包括数字、大小写字母等,但不能出现空格。
Special Judge 返回信息说明
本题采用 Special Judge 判断你的答案是否正确。
checker.cpp 将会以 \(\texttt{Score=}\text A,\texttt{Type=}\text B\) 的方式返回信息。
\(\tt Score\) 类表示你的得分情况,\(\text A\) 有以下取值:
- \(\text A=1\),表示含义如下:
\(\text{Accepted.} \texttt{ Your Ans and SM are both proper.}\)
代表 \(T\) 组答案全部符合要求。 - \(\text A=2\),表示含义如下:
\(\text{Partially Correct.}\texttt{ All Ans are right.}\)
表示该测试点中你的回答中 \(x\) 全部正确,你能得到该测试点 \(20\%\) 的分数。 - \(\text A=3\),表示含义如下:
\(\text{Partially Correct.}\texttt{ You pass 70\% tests!}\)
表示该测试点中你的回答正确的组数不少于\(\lfloor0.7\times T\rfloor\)(\(x\) 与乐谱均符合要求),你能得到该测试点 \(10\%\) 的分数。 - \(\text A=4\),表示该测试点你只能拿到 \(0\) 分。
\(\tt Type\) 类表示你的得分情况,\(\text B\) 有以下取值:
- \(\text B=0\),表示你的答案全部正确,与 \(\text A=1\) 配对。
- \(\text B=1\),表示含义如下:
\(\text{Wrong Answer.}\texttt{ The length of your SM is not right!}\)
代表你在一组数据中构造的乐谱的长度不为 \(n\)。 - \(\text B=2\),表示含义如下:
\(\text{Wrong Answer.}\texttt{ Your Ans is not right!}\)
代表你在一组数据中 \(x\) 的值错误。 - \(\text B=3\),表示含义如下:
\(\text{Wrong Answer.}\texttt{ Your Ans and SM are not matched!}\)
代表你在一组数据中构造的乐谱使 Tpes 产生的愤怒值不为 \(x\)。
这里 \(\text{Ans, SM}\) 分别表示 Answer(\(x\) 的值)和 Sheet Music(乐谱)。
注意到 \(\tt Type\) 只会反映你在该测试点中第一次错误的类型。
题解:
读到题的时候还以为是teto那个重音
题意
对于一个长度为 \(n\) 的字符串 \(p\),当有相邻的两个字符相同,即 \(p_i=p_{i+1}\) 时,则让愤怒值增加 \(a\);当连续 \(k\) 个字符没有相邻且相同的字符,即在 \([p_i,p_{i+k}]\) 中,不存在 \(p_i=p_{i+1}\) 时,则让愤怒值增加 \(b\)。
请你求出如何构造这个串,使得愤怒值最小。
思路
先考虑贪心,如果对于一个没有特殊性质的串,怎样可以尽可能让成本更小:
- 如果 \(a\) 远远小于 \(b\),我们不妨在每个子段长度即将达到 \(k\) 的时候加入一个重音,这样就可以维护最少的重音个数。
- 如果 \(a\) 远远大于 \(b\),那么就抛弃重音吧!
实现
有了大致的思路,就可以开始着手构造了!
首先考虑一下特殊的情况:
- 对于 \(k>n\) 的情况,永远不会出现愤怒值增加 \(b\) 的情况,此时我们只需要考虑尽可能减少重音,那么构造一个相邻字符全都不相同的串就可以了:
if(k>n){
cout<<"0\n";//别忘了输出代价!
for(int i=1;i<=n;i++){
if(i&1)putchar('1');
else putchar('0');
}
cout<<"\n";
continue;
}
在分析一般情况之前,我们需要想一下怎样达到上文中维护最少的重音个数的效果:我们肯定要保证不存在情况 \(b\) 的同时尽可能让每个重音间隔的更远,也就是说每个重音的距离应该为 \(k-1\)。但是这里要注意一个重音在原串的长度为二。
- 如果有重音 TA 就会很愤怒,即 \(b\times(k-1)\le a\),这就意味着对于任意的重音,无论它出现在哪里,都会让愤怒值增加。此时我们同样只需要构造一个相邻字符全都不同的串就可以了,此时代价为 \(b\times(n-k+1)\):
if(b*(k-1)<=a){
cout<<(long long)b*(n-k+1)<<"\n";
for(int i=1;i<=n;i++){
if(i&1)putchar('1');
else putchar('0');
}
cout<<"\n";
continue;
}
- 剩下的最后一种情况,要保证每一段长度为 \(k\) 的子串里面都存在一个 \(a\)。
当这个子段在开头的时候,只需要在右端点 \([k-2,k]\) 这里放入一个重音就可以维护 \([1,2k-2]\) 的相邻不相同串。
那为什么是 \(2k-2\) 这么一个并不美丽的数呢?因为他的后面一个数 \(2k-1\) 作为右端点时,会把原本的重音 \([k-2,k]\) 切开,使其不再构成重音。
因此,在处理中间段的时候,我们需要将串长视为 \(k-1\),即上一段的重音被切开了,占用了此段的一个位置。同时重音还会占据两个位置,也就是说我们维护的相邻不相同串长度将变成 \(k-3\)。
else{
for(int i=1;i<=n;i++){
if(i%3==1)s[i]=5;
else if(i%3==2)s[i]=1;
else s[i]=4;
}
关于最后一段,我们需要看看它给出一个重音来维护更优还是保留最后的长串更优。
long long q=((n-1)/(k-1)); // 重音数
long long c=n-q*(k-1);
long long lst=b*c;
if(lst<=a){ // 尾段处理
cout<<((q-1)*a+lst)<<"\n";
for(int i=k,j=1;j<q;i+=k-1,j++)s[i]=s[i-1];
}
else{
cout<<q*a<<"\n";
for(int i=k,j=1;j<=q;i+=k-1,j++)s[i]=s[i-1];
}
for(int i=1;i<=n;i++)cout<<s[i];cout<<"\n";
}
AC 代码
#include<bits/stdc++.h>
using namespace std;
long long n,k,t,a,b;
int s[200005];
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
cin>>n>>k>>a>>b;
if(k>n){
cout<<"0\n";
for(int i=1;i<=n;i++){
if(i&1)putchar('1');
else putchar('0');
}
cout<<"\n";
continue;
}
if(b*(k-1)<=a){
cout<<(long long)b*(n-k+1)<<"\n";
for(int i=1;i<=n;i++){
if(i&1)putchar('1');
else putchar('0');
}
cout<<"\n";
continue;
}
for(int i=1;i<=n;i++){
if(i%3==1)s[i]=5;
else if(i%3==2)s[i]=1;
else s[i]=4;
}
long long q=((n-1)/(k-1));
long long c=n-q*(k-1);
long long lst=b*c;
if(lst<=a){
cout<<((q-1)*a+lst)<<"\n";
for(int i=k,j=1;j<q;i+=k-1,j++)s[i]=s[i-1];
}
else{
cout<<q*a<<"\n";
for(int i=k,j=1;j<=q;i+=k-1,j++)s[i]=s[i-1];
}
for(int i=1;i<=n;i++)cout<<s[i];cout<<"\n";
}
return 0; // 好习惯
}

浙公网安备 33010602011771号