一.A. Phoenix and Balance
1.大意:
有n个数,分别是从2到2^n,将他们随机分成2堆(每堆都是n/2个),保证n是偶数,让两堆n/2个数的和的差最小
2.题解:
找规律,发现分成一堆为:2n/2,2n/2+1,…,2n-1,(共n/2个数),
另一堆为·:21,22,…2n/2-1,2n,
这种情况两边和相差最小
推公式得:2n/2+1-2
用pow会T,用了快速幂看了别人的方法,发现也可以是前一个数*2+2,实质差不多
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
//long long int dp2[400000],dp1[400000],a[400000];
using namespace std;
typedef long long ll;
ll quick_pow(ll a,ll b) {
ll ans = 1;
while(b) {
if(b&1)
ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
int main(){
int t;
int n,i;
long long int a[40];
for(i=2;i<=32;i+=2){
a[i]=quick_pow(2,i/2+1)-2;
}
while(cin>>t){
while(t--){
cin>>n;
cout<<a[n]<<endl;
}
}
}
二.G. Odd Selection
1.大意:
有一个n个数的数组,现在从n个数中选x个数,让他们的和是奇数,如果可以就yes,不可以no
2.题解:
首先排除几个肯定不可以的特殊情况:
1.全偶
2.全奇,但x是偶数
3.n==x时,总和是偶数/奇数是偶数个的
剩余情况,题目保证x<n,则总可以找到先选偶数,根据偶数调整奇数个数,使最终结果为奇数的
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
//long long int dp2[400000],dp1[400000],a[400000];
using namespace std;
typedef long long ll;
int main(){
int n;
int t;
int x;
int odd,even;
int a[1001];
while(cin>>t){
while(t--){
odd=0;
even=0;
cin>>n>>x;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]%2==0)
even++;
else
odd++;
}
if(n==even)
printf("No\n");
else if(n==odd && x%2==0)
printf("No\n");
else if(n==x&&odd%2==0)
printf("No\n");
else
printf("Yes\n");
}
}
}
三.H. Subsequence Hate
1.大意:
现在有个字符串s里只有0/1,如果字符串的子序列有“010”/“101”的,这个字符串就不是好的,现在可以调整字符串某些位置的数,把0->1 / 1->0,使这个字符串是好的,问最少要变几个,可以一个都不变
2.题解:
显然最终要实现,左边全1右边全0,或者左边全0右边全1,
算是dp的思想,两个dp,一个记录第i个位置上左边为1右边为0需要改变的数字,另一个记录另一种情况,找最小的
用sum0记录所有的0,sum1记录所有的1
x0记录该位置即之前的0,x1为。。的1,
若左1右0,则需改变:x0+sum1-x1
若左0右1,则需改变:x1+sum0-x0
找最小
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
//long long int dp2[400000],dp1[400000],a[400000];
using namespace std;
typedef long long ll;
int main(){
int t,n,i,j;
char s[2000];
while(cin>>t){
while(t--){
scanf("%s",s);
n=strlen(s);
int sum0=0,sum1=0;
int x0=0,x1=0;
for(i=0;i<n;i++){
if(s[i]=='0')
sum0++;
else
sum1++;
}
int out=2000;
for(i=0;i<n;i++){
if(s[i]=='0')
x0++;
else
x1++;
out=min(out,min(x1+sum0-x0,x0+sum1-x1));
}
cout<<out<<endl;
}
}
}
四.B. Phoenix and Beauty
1.大意:
给定n个数,你可以往里面任何位置加任何数,使该数组连续m个数的和相同,若可以,输出数组数的个数和数组里的数,若不可,则输出-1
2.题解:
当n个数中有>m个不同的数时,则不可,假设m=3,数组为a,b,c,d,a,b,c,d则会有a+b+c!=b+c+d
当n个数有m种数时,复制m个数n次即可
当n个数有<m种数时,补充至m种,将m种数重复n次
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#define N 300010
using namespace std;
typedef long long ll;
int a[N],b[N];
set<int> s;
int main(){
int t,n,k;
int i,j;
while(cin>>t){
while(t--){
s.clear();
cin>>n>>k;
memset(b,0,sizeof(b));
memset(a,0,sizeof(a));
for(i=0;i<n;i++){
cin>>a[i];
s.insert(a[i]);
}
int n1=s.size();
if(n1>k){
printf("-1\n");
continue;
}
j=1;
while(s.size()<k){
s.insert(j);
j++;
}
set<int>::iterator iter;
int x=0;
for(iter=s.begin();iter!=s.end();iter++){
b[x++]=*iter;
}
printf("%d\n",n*x);
for(i=0;i<n-1;i++){
for(j=0;j<x;j++){
printf("%d ",b[j]);
}
}
for(j=0;j<x-1;j++){
printf("%d ",b[j]);
}
printf("%d\n",b[j]);
}
}
}
五.I. Game On Leaves
1.大意:
给一个n个节点的无根树,两个人轮流取,每次能取叶子节点并删除和他相连的边,谁取得了x,谁就赢
2.题解:
博弈,每个人都会选择最利于自己的,
注意:如果x一开始就是叶子节点,甚至该树只有一个节点,先手胜
剩下情况中与x相连的边>=2,两人都最优解法的情况下,最终会变成下图
此时先手必败。
由于两人都不想拿完子树,否则x就成为了叶子,所以两个人一定会留下两棵子树,并且一直拿到实在没有办法了才会把x变成叶子。于是就是上面这种情况了。则只需判断n的奇偶性就可判断谁赢
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#define N 300010
using namespace std;
typedef long long ll;
int a[N],b[N];
int main(){
int t;
int x,n;
int u,v;
int i;
while(cin>>t){
while(t--){
memset(a,0,sizeof(a));
cin>>n>>x;
for(i=0;i<n-1;i++){
cin>>u>>v;
a[u]++;
a[v]++;
}
if(a[x]<=1||n%2==0){
printf("Ayush\n");
}else{
printf("Ashish\n");
}
}
}
}
六.C. Phoenix and Distribution
1.大意:
给一个长为n的字符串,将他分为k份,可以调顺序,使得字典序最大的子序列最小
字典序大小:a<b,ab<abb,abc<ac…
该题意思是,这k个子字符串,一定有个是字典序最大的,让这个字典序最大的尽可能小
2.题解:
参考
首先将该字符串排序,按此划分
要想最大的最小,一定是前k个和后n-k个相结合的形式,字典序会尽可能小
判断前k个字母是否相同,如果不同,那么第s[k]所在的字符串一定是字典序最大的,让他最小的方式是,该字符串只有s[k],把剩余n-k个字符分给前面小于s[k]的字母,比如abbb|cccc,分为accc,b,b,b
如果前面的全相同,判断后面n-k个字符是否相同
如果相同,如aaa|bbbb,则将后面n-k个均匀分配
如果有不同,如aaa|bbc,则将后面的分给一个a,因为n-k个不同的肯定有大也有小,如果分开,就会让原本在后面的大的跑到前面变得更大,按字典序排好的放一起反而最小,就如ac>abbc
3.ac代码:
#include<stdio.h>
#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#define N 300010
using namespace std;
typedef long long ll;
char s[N];
int main(){
int t;
int n,k;
int i;
while(cin>>t){
while(t--){
cin>>n>>k;
scanf("%s",s);
sort(s,s+n);
int flag=0;
for(i=1;i<k;i++){
if(s[i]!=s[i-1])
flag=-1;
}
if(flag==-1){
printf("%c\n",s[k-1]);
}else{
for(i=k+1;i<n;i++){
if(s[i]!=s[i-1])
flag=-1;
}
if(flag==-1){
printf("%c",s[k-1]);
for(i=k;i<n;i++){
printf("%c",s[i]);
}
printf("\n");
}else{
int x=(n-k)/k;
printf("%c",s[k-1]);
for(i=k;i<k+x;i++){
printf("%c",s[i]);
}
if((n-k)%k!=0)
printf("%c",s[k]);
printf("\n");
}
}
}
}
}