Codeforces Round #638 (Div. 2) 萌新题解~
出题人的官方题解~
这次的A-D都是贪心哇!
A. Phoenix and Balance
题意:以2为首项,2为公比的等比数列,要求分成两个数列,使得这两个数列和的差最小。
tips:可以发现,这个等差数列的第n项比之前的和都大,所以肯定是选第n项和前n/2-1项组成一个数列,剩下的是另一个数列。
#include
#define ll long long
using namespace std;
int t,n;
ll quick_pow(int x, int n){
ll res = 1;
while(n){
if(n & 1) res = res * x;
n >>= 1;
x *= x;
}
return res;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ll ans = 0,tmp1,tmp2;
tmp1 = 2 * (quick_pow(2, n - 1) - 1);
tmp2 = 2 * (quick_pow(2, n / 2 - 1) - 1);
printf("%lld\n",quick_pow(2, n) - tmp1 + 2 * tmp2);
}
return 0;
}
P.S.对2的幂可以直接用位运算做,不用快速幂~做的时候傻了。
B. Phoenix and Beauty
题意:添加任意不超过n的数字,使得所有长度为k的子序列和相等。
如: 1 2 2 1 k=3,则 添加为1 2 1 2 1,和为4.
tips:注意到必须是周期性的才可以。而且出现的不同数字不可以多于k个(否则不可能构造出周期为k的数列)。注意不用最短。
自己写的太丑了所以不放了 思路更麻烦,就是不断按周期重复出现的数字
答案的思路非常巧妙哇!如果原数列恰好k个不同数字,直接重复输出就好;如果不足k个,就在每一个周期后面补k-size个1(or任意数字).构成一个周期为k的数列。这样原来数列的n个数,每一个必定对应1个周期(因为不要求最短)。
#include
using namespace std;
void solve(){
int N,K;
cin>>N>>K;
set<int>s;
for (int i=0;i<N;i++){
int a;
cin>>a;
s.insert(a);
}
//if more than K distinct numbers, print -1
if (s.size()>K){
cout<<-1<<endl;
return;
}
cout<<N*K<<endl;
for (int i=0;i<N;i++){
//print the distinct numbers
for (int b:s)
cout<<b<<' ';
//print the extra 1s
for (int j=0;j<K-(int)s.size();j++)
cout<<1<<' ';
}
cout<<endl;
}
int main(){
int t; cin>>t;
while (t--)
solve();
}
C. Phoenix and Distribution
题意:给一个字符串,要求将其中的字母任意顺序排列组合成k个字符串(不要求是原来串的子串),使得这k个串中字典序最大的串最小~
tips:一开始的算法错了~
注意分类讨论:
1.如果排序后book[1] != book[k] 则答案就是book[k](可以把后面的都放在第一个串里);
把book[1]-book[k]分到k个串中,然后从第k+1个开始:
2.如果后面的不都相同,则全部放到第一个串,即为答案(注意与3区分);
3.如果后面的都相同,则尽可能平分到k个串中,最后多于的放到第一个串,第一个串即为答案。
e.g. aaaab k = 2 应该输出aaab;而 aaaaa k = 2 则应该输出aaa而不是aaaa。
一开始的错误想法是如果某个字母的数量是k的倍数就平分。可以举出反例:
aaabbbccc k = 3, 应该输出abbbccc而不是abc。
#include
using namespace std;
int t,n,k;
char c,book[100005];
void solve(){
scanf("%d%d",&n,&k);
getchar();
for(int i = 1; i <= n; ++i)
scanf("%c",&book[i]);
sort(book + 1, book + 1 + n);
if(book[1] != book[k]){ //对应上述情况1
printf("%c\n",book[k]);
return ;
}
printf("%c",book[1]); //对应上述情况3
if(book[k + 1] == book[n]){
int tmp = ceil((double)(n - k) / k); //注意要cast。否则两个整数相除……
for(int i = 1; i <= tmp; ++i)
printf("%c",book[k + 1]); //对应上述情况2
}else{
for(int i = k + 1; i <= n; ++i)
printf("%c",book[i]);
}
putchar('\n');
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
D. Phoenix and Science
题意:细菌初始质量是1,初始个数是1.白天任意数量的细菌可以选择分裂(质量平分成两个)。晚上每个细菌质量+1.问可否恰好经过若干天,所有细菌质量之和为x。(肯定可以。哪怕不分裂一直1+1+1都可以)如果可以,求最短天数和每天多少个细菌分裂。
tips:
1.注意分裂在白天,增重在晚上,二者不冲突;
2.分裂可以只有一部分分裂。
观察可以发现,如果一直只有一个细菌,则每次增重是1,总重量是1+1+1+……1
如果分裂成两个,则总重量:1+2+2+……+2
以此类推,最短的天数就是每天都全部分裂(而且增长是一个2为公比的等比数列),直到某一天如果全部分裂会超出要求。然后排序,每天分裂的数量就是book[i] - book[i-1](如果不分裂,book[i] == book[i-1],多的代表多分裂的个数。因为每分裂一个,当天晚上总重量+1.book数组代表当天增加的重量)
e.g. 20可以分成1 2 4 8 5,排序后变成1 2 4 5 8
则:第一天分裂1个,第二天分裂2个,第三天分裂1个(5-4),第四天分裂3个(8-5)。
贪心代码:
#include
using namespace std;
int t,n,cnt,book[100005];
void solve(){
scanf("%d",&n);
cnt = 0;
for(int i = 1; i <= n; i <<= 1){
book[++cnt] = i;
n -= i; //这步很重要,时刻更新当前还需要的n值(还需的重量)
}
if(n > 0) book[++cnt] = n;
sort(book + 1, book + 1 + cnt);
printf("%d\n",cnt - 1);
for(int i = 1; i < cnt; ++i)
printf("%d ",book[i + 1] - book[i]);
putchar('\n');
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}