2021-01-13训练题解

contest

一.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");
				}
				
			}
		}
	}
	


} 

七.D. Phoenix and Science

1.大意:

2.题解:

3.ac代码:

猜你喜欢

转载自blog.csdn.net/weixin_46064382/article/details/112703786