Codeforces Round #779 (Div. 2) 3.29 A~D 题解

Codeforces Round #779 (Div. 2) 3.29 A~D 题解

A. Marin and Photoshoot

题意

给你一段01字符串,要求每个大于等于2的字串中,1的个数要大于等于0的个数,可以在任何位置插入1,问最少插入多少个1使要求成立

思路

分类讨论

  1. 当存在 0110 的字串时,无论怎么分割 ,1的个数大于等于0 ,所以添加数为0
  2. 当存在 010 的字串 ,需要添加1在两个0中间,变成0110,所以添加数为1
  3. 当存在 00的字串时,则要变成0110,添加为2

显而易见 当相邻两个0之间存在两个以上的1时,要求成立

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,A[N]; 
string s;
int main(){
	int T;
	cin>>T;
	while(T--){
		cin>>n>>s;
		int cnt1=0,cnt2 = 0;
		for(int i = 1;i<n;i++){
			if(s[i]=='0'&&s[i-1]=='0')cnt1+=2;
			else if(i>=2&&s[i]=='0'&&s[i-1]=='1'&&s[i-2]=='0')cnt1++;
		}
		cout<<cnt1<<endl;
	}
	return 0;
}

B - Marin and Anti-coprime Permutation

题意

定义一种排列(P1,P2,P3,Pn)时漂亮的,gcd(1⋅P1,2⋅P2,…,n⋅Pn) > 1,问给定的n有多少种不同的排列

思路

显然,让gcd(1⋅P1,2⋅P2,…,n⋅Pn) > 1最简单的方式就是让每个数都是偶数,最后的gcd为2,就是说让每个奇数都乘上一个偶数

分类讨论

  1. 当 n 为奇数时,总会存在一个奇数无法满足要求,例如 {1,2,3} ,奇数大于偶数,所以相乘时会至少一个奇数,所以当n为奇数时,答案为0
  2. 当n为偶数时,只需要让奇数随便乘上一个偶数,偶数随便乘上一个奇数,对于排列中的偶数来说,方案数为 n/2 的阶乘,奇数也是n/2的阶乘 , 答案即为(n/2)!*(n/2)!%mod

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,A[N]={1}; 
string s;
ll mod = 998244353;
int main(){
	int T;
	cin>>T;
	for(int i = 1;i<=1000;i++){ // 预处理阶乘
		A[i] = A[i-1]*i%mod;
	}
	while(T--){
		cin>>n;
		if(n%2==1) cout<<"0\n";
		else {
			long long ans = A[n/2]*A[n/2]%mod;
			cout<<ans<<endl;
		} 
	}
	return 0;
}

C. Shinju and the Lost Permutation

定义一个排列的power , 前面的最大值会覆盖后面比它小的值,power就是这个数组的不同数字的多少,(我也说得不清楚), 例如 {2,1,4,5,3} 形成的数组为{2,2,4,5,5} ,所以power为 5
{4,3,1,5,2} 形成数组为{4,4,4,5,5} power 为 2
定义第 i 位置数组滑动,数组从 p=[p1,p2,…,pn] 变成p=[pn−i+1,…,pn,p1,p2,…,pn−i].
问给出1~n位置数组滑动后的power 值,是否存在一种初始排列成立

思路

  1. 数字 1 必然存在,且只有一个,因为排列只有一个最大值
  2. 由于数组是可以滑动的,所以我们可以把序列变成以1开始的,这两者是等价的, 例如{4,5,1,2,3}可以看成{1,2,3,4,5}
  3. 当从1开始时,后面滑动数组时power增加时最多增加1,而power减少可以任意,因为可能有较大数字使power骤减

具体写法

首先 判断是否只有一个1 ,不是输出NO
然后 从1开始遍历,遍历到末尾再从头遍历到1 ,比较后者与前者的大小,当后者大于前者并且两者相差2以上则输出NO

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,A[N]={1}; 
string s;
ll mod = 998244353;
int main(){
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		int ix = -1;
		int pre = 1,ok=1;
		int kk = 0;
		for(int i = 0;i<n;i++){
			cin>>A[i];
			if(A[i]==1) kk++,ix = i;
		}
		if(kk!=1) ok = 0;
		for(int i = ix;i<n;i++){
			if(A[i]>pre&&A[i]-pre>1) ok = 0;
			pre = A[i];
		}
		for(int i = 0;i<=ix;i++){
			if(A[i]>pre&&A[i]-pre>1) ok = 0;
			pre = A[i];
		}
		if(ok)cout<<"YES\n";
		else cout<<"NO\n";
	}
	return 0;
}

D1 - 388535 (Easy Version)

题意

给定排列范围为l~r,长度为r-l+1,你可以选一个数x,对所有该排列的数异或x,得到一个新的数组,问你已经得到了异或后的数组,输出一个合法的x。

思路

首先看对一个排列都异或上一个数的性质,对于二进制中每一位来说,异或上1会使该位进行翻转,那对全部的数异或上1时,那么这一位的1和0的数量就会交换

做法

所以我们只需把初始排列,和最终的排列每一位1的数量记录下来,每一位进行比较,如果说有某一位上初始排列上的1数量与最终排列上的1不同,在最后的答案中该位为1,否则为 0

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n; 
ll A[N],B[N];
ll bit[N],bit2[N];
void slove(){
	ll l,r;
	cin>>l>>r;
		for(int i = 0;i<=r;i++){
			cin>>B[i];
			for(int j = 0;j<30;j++){
				if(B[i]>>j&1) bit2[j]++;
				if(i>>j&1) bit[j]++; 
			}
		}
		ll ans = 0;
		for(int i = 0;i<30;i++){
			if(bit[i]!=bit2[i]) ans += (1<<i);
			bit[i] = bit2[i] = 0;
		}
		cout<<ans<<endl; 
}
int main(){
	int T;
	cin>>T;
	while(T--){
		slove();
	}
	return 0;
}

你可能感兴趣的:(codeforce,算法,c++)