Codeforces Round #638 (Div. 2) 萌新题解

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;
}

你可能感兴趣的:(Codeforces Round #638 (Div. 2) 萌新题解)