05.16codeforeces训练记录

题目一:1819A - Constructive Problem.

大意:MEX(a)代表数组a中,没有出现的最小的自然数。现在出长度为n的数组a,能够进行一次操作:将[L,R]内的元素都赋值为k。现询问能否进行一次操作,使得操作后的数组a'有MEX(a') =  MEX(a)+1。

思路:由题意得,MEX(a)最大的情况为,a为0,1,...,n-1。  故MEX(a) <=n。 不妨设MEX(a) = k。

因为原数组中没有k,故操作后,需要使某个区间的元素为k。

由于MEX(a')= k+1, 故若原数组中存在元素a[i] = k+1 ,则至少需要找出k+1元素的左右边界,赋值为k。同时需要判断左右边界确定的区间赋值为k时,是否使得0~k-1某个出现的次数为0。

若原数组中不存在元素a[i] = k+1 ,则判断是否有元素a[i] >=k+2, 有则只需要该元素赋值为k即可满足题意。若不存在该元素,则判断0~k-1的元素中是否有某个元素出现至少两次,有则只需要该元素赋值为k即可满足题意,反之无法满足题意。

找MEX(a)的方法:从0到大开始遍历,第一个没出现的元素即为MEX(a) 

以下是我的AC代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 2e5 +10 ,mod = 1e9 +7   ; 
LL a[N] ,b[N] ;


int main()
{

	int T ;cin >> T;
	while( T--){
		int n ;
		cin >> n  ;
		unordered_map<int,int> mp;
		mp.clear();
		for(int i  = 0 ; i<n ;++i){
			cin >> a[ i] ;
			mp[ a[i]] ++; 
		}
		int mex = n; 
		for(int i = 0;  i<n; ++i){
			if( !mp[i] ) {
				mex = i ;break;
			}
		}
		bool flag = true;
		if( mp[mex+ 1] ) {
			int l = -1, r= -1;
			 for(int i = 0; i <n; ++i){
			 	if( a[i] == mex+1) {
			 		if( l == -1) l = i;
			 		r = i ;
				 }
			}
			for(int i = l ; i <=r; ++i){
				mp[ a[i] ] -- ; mp[ mex] ++ ;
			}
			
			for(int  i= 0; i<=mex ; ++i) if( !mp[i]) flag =false;
			if( mp[mex+1]) flag = false;
		}else{
			bool bigger = false;
			for( auto t :mp){
				if( t.x>=mex +2 && t.y) bigger  = true;
			}
			//cout<<bigger<<endl;
			if( !bigger){
				flag = false;
				for(int i = 0 ;i< mex; ++i) if( mp[i] >1) flag=  true;
			}
		}
		if( flag) cout<<"YES"<<endl;
			else cout<<"NO" <<endl;
		
	} 
    return 0 ; 
}




更为简洁的方法:(可能会TLE)

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL a[N];


int main()
{

	int T ;cin >> T;
	while( T--){
		int n ;
		cin >> n ;
		unordered_map<int,int> l ,r ;
		bool flag = true;
		for(int i =1 ;  i<=n; ++i) {
			scanf("%d",&a[i]) ;
			if( !r[a[i]]) l[a[i]] = i;
			r[a[i]] = i ;
		}
		int k = 0;
		//找到第一个没出现的元素MEX(a)  
		while( l[k]) ++k;
		//若k+1不在原数组中,此时若 k=n,说明原数组是0~n-1的排列,无法完成条件
		//若k不等于n , 说明原数组中必存在元素大于2 或者0~k-1中有元素出现次数大于2  ,必能满足题意 
		if( !l[k+1] ) flag  = ( k!=n) ; 
		//判断0~k-1中是否存在因为把k+1都赋值为k,出现次数变为0的元素 
		else for(int i =0 ; i<k ; ++i){
				if( l[i] > l[k+1] && r[i] < r[k+1] ) {
					flag = false; break;
				}
			}
		if( flag) cout<<"YES\n" ;
			else cout<<"NO\n" ;
	} 
    return 0 ; 
}



题目二:1738C - Even Number Addicts

大意:有长度为n的数组,Alice和Bob两人分别从中拿一个数,每个人都按最好方案拿,若拿完后,Alice手中所有数之和为0 ,则Alice胜利,反之Bob胜利。现给出一个数组,Alice先手拿球,要求判断谁会获得胜利。

思路: 以下A代替Alice,B代替Bob。A要胜利,要保证手中为偶数。B要胜利,则需要使A手中为奇数。而偶数并不影响奇偶性,故偶数的个数最终会是0或1(AB可依次取偶数得到该结果)。

设有m个奇数,显然,每4个奇数为一轮(A与B手中都至少有2的整数倍个奇数,使得手中和为偶数)。当m%4 = 3时,由于A先出手,故A会拿到2个奇数,使得手中数为偶数。A必胜

当m%4 = 2 时,由于A先出手,故A会拿到1个奇数,使得手中数为奇数。B必胜。

当m%4 = 0 时, 此时AB手中数都为偶数,A必胜

当m%4 =1时, 以m=1为例,若此时有1个偶数,A必胜;若只有0个,则B必胜。

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 2e5 +10 ,mod = 1e9 +7   ; 
LL a[N] ,b[N] ;


int main()
{

	int T ;cin >> T;
	while( T--){
		int n ;
		cin >> n  ;
		int cnt= 0 ; 
		for(int i = 0;  i<n; ++i){
			int x ;cin >>x;
			if( x &1 ) cnt ++ ; 
		}
		bool flag = true ;
		if( cnt %4 == 1 ){
			if( n&1) flag = false;
		} else if( cnt %4 ==2 ){
			flag = false;
		}
		if( flag) cout<<"Alice"<<endl;
			else cout<<"Bob"<<endl;
	} 
    return 0 ; 
}




题目三:1808B - Playing in a Casino

大意:给定n个m维向量,计算各个向量间的曼哈顿距离的和。 即求每个向量|a1−b1|+|a2−b2|+⋯+|am−bm|的和。

思路:将每个向量同个维度的元素ai,bi分离出来放到同一个数组中。并将该数组从小到大排序以去掉绝对值,则该维度上的曼哈顿距离为从1到n求和, num[j]*(j-1) - sum[j-1] 。   其中,sum为前缀和。

代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL a[N] ;

int main()
{

	int T ;cin >> T;
	while( T--){
		int n ,m ;
		cin >> n >> m;
		vector< vector<int> > num( m ,vector<int>(n )  );
		for(int i= 0 ; i<n ; ++i){
			for(int j =0;  j < m ; ++j){
				cin>> num[j][i] ;
			}
		}

		LL res= 0 ; 
		for(int i = 0; i<m ;++i){
			for(int j =1; j<=n ; ++j){
				a[j]  = num[i][j-1] ;
			}
			sort( a+1 , a+1+n) ;
			//for(int j =0 ; j <=n; ++j) cout<<a[j] <<" " ;cout<<endl;
			for(int j =1; j<=n ; ++j)
				sum[j] = a[j] + sum[j-1] ;
			for(int j =2 ;j<=n; ++j){
				
				res += (j-1)*a[j] - sum[j-1] ;
			}
		}
		cout<< res <<endl;
	} 
    return 0 ; 
}




题目四:1807E - Interview

大意:一个数组a中存在一个特殊元素, 该特殊元素在数组中的值为a[i],但参与运算时却为a[i]+1,现要求你在不多于30次询问的情况下,找出该特殊元素

思路:二分范围,使得最终范围的左右边界相等,即为该特殊元素

代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL a[N] ;


int main()
{

	int T ;cin >> T;
	while( T--){
		int n ; 
		cin >> n ; 
		for(int i = 1; i <= n ; ++i)
			cin >> a[i] ;
		for(int i = 1; i <=n; ++i)
			sum[i] = sum[i-1] + a[i ] ;
		int l = 1 ,r  = n ;
		while( l!=r){
			
			int mid = (l+r)/2;
			cout<<"? "<<mid-l+1<<" ";
			for(int i=l ; i<=mid ;++i) cout<<i<<" "; cout<<endl;
			int res = 0;
			cin >> res ; 
			if( res > sum[mid]-sum[l-1] ) {
				r= mid ;
			}else {
				l = mid+1;
			}
		}
		cout<<"! "<< l <<endl;
	} 
    return 0 ; 
}




题目五:1805C - Place for a Selfie

大意:有n个经过原点的直线y=kx,与m个开口向上的抛物线,系数为a,b,c。现在要求在输入完n条之间后,每次输入一条抛物线,则判断该抛物线是否在某条直线的一侧(即不相交)。

思路:对斜率k从小到达排序,根据判别式,若有(b-k)^2 < 4ac,则满足题意。故二分寻找第一个大于等于b的元素 , 若该点附近的元素也满足判别式, 则满足题意。反之,则不满足。

代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL k[N];
LL a, b ,c;

int main()
{

	int T ;cin >> T;
	while( T--){
		int n ,t; 
		cin >> n >>  t ; 
		for(int i =0 ; i<n; ++i)
			cin >> k[i]  ;
		sort( k , k+n ) ; 
		for(int i = 0 ; i<t ; ++i){
			cin >>a >>b >> c ;
			int idx = lower_bound( k, k+n , b) -k ;
			//cout<<"--"<<idx<<" "<<k[idx]<<endl;
			if( idx <n  && ( k[idx] - b)*(k[idx]-b) < 4*a*c) {
				cout<< "YES"<<endl<< k[idx ]<<endl;continue;
			}
			if( idx > 0  && ( k[idx-1]-b)*(k[idx-1]-b) < 4*a*c ){
				cout<<"YES" <<endl<<k[idx-1] <<endl;
				continue;
			}
			cout<<"NO" <<endl;
		}
		cout<<endl;

	} 
    return 0 ; 
}




问题六:1804C - Pull Your Luck

大意:一个轮盘有n个扇形区间,标记为0~n-1。初始指针指在x处, Vesper 最大能使出小于等于p大小的力。注意,力不能等于0!

使出i的力时,轮盘能走i+(i-1)+..+1= i*(i+1)/2个扇形区间。现给出n,x,p,问是否能使得指针最后指向0。

思路:当使出k力,有k*(k+1)/2 >2n时,由于1+2+...+2n = n*(2n+1)  ,模上n为0.故每2n为一个循环。故先打表,求使出1~2n力时的结果。注意需要使用long long ,因为当i到达1e5+时会爆int。

要使的指在x的指针指向0,需要有 i*(i+1)/2 %n = (n-x) %n (取模考虑到x等于0的特殊情况)

故需要在1~2n中找出是否满足题意的力的最小值,若找不到则不能满足题意。

找到时,比较p与该力的大小,p若小于该力,则不满足题意,反之满足。

代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL a[N];


int main()
{
//	for(int n = 1 ;n <=10 ; ++n){
//		cout<<"n = " <<n <<" : ";
//		for(int i = 1; i <= 20; ++i){
//			cout<< i*(i+2)/2 % n <<" " ;
//		}
//		cout<<endl;
//	}
	int T ;cin >> T;
	while( T--){
		LL n ,x,p; 
		cin >> n>> x >> p ;
		for(LL i = 0 ; i < 2*n ; ++i){
			a[i ]  = (i+2)*(i+1)/2  % n ;
			//cout<<a[i]<<' '; 
		}
		//cout<<endl;
		LL res= -1; 
		for(int i = 0 ; i< 2*n ;++ i){
			if( a[i ] == (n-x)%n ) {
				res = i+1; break;
			}
		}
		//cout<< res <<endl;
		if(  p>=res && res != -1  ) cout<<"YES" <<endl;
			else cout<<"NO" <<endl;  
	} 
    return 0 ; 
}




题目七:1800E2 - Unforgivable Curse (hard version)

大意:给定长度为n的两个字符串s,t ,并给出k,每次操作,能使字符串中的任意字符与距离为k或k+1的字符交换位置。问不限制操作次数的情况下,能使的s等于t吗。

思路:

当n<=k 时,字符串中的字符不能变动,只能判断s,t是否相等。

当n>k时;

在字符串中的字符能与其他任意字符互换情况的下,只需判断s,t是否为同一个排列即可。

当字符串中的字符不能与其他任意字符互换时,不妨设i位置上s[i]不能互换,则有 i-k< 0 && i+k>=n  ,则得出 n-k <= i <k ,故当n-k < k时,[n-k,k)上的元素不能任意互换,故需要判断这个区间上s与t的字符是否相同。若存在不同,则s不能通过操作等于t。

若全部相同,则只需判断s与t是否为同一个排列即可。

代码:

#include <iostream>
#include <cstring>
#include <queue> 
#include <algorithm>
#include <unordered_map>
#define x first
#define y second 
#define PII pair<int,int> 
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7   ; 
LL sum[N] ;
LL a[N];


int main()
{
//	for(int n = 1 ;n <=10 ; ++n){
//		cout<<"n = " <<n <<" : ";
//		for(int i = 1; i <= 20; ++i){
//			cout<< i*(i+2)/2 % n <<" " ;
//		}
//		cout<<endl;
//	}
	int T ;cin >> T;
	while( T--){
		int n , k ;
		cin >> n >> k;
		string s,t;
		cin >> s >> t ;
		if( n <= k) {
			if(strcmp( s.c_str() ,t.c_str() ) != 0 ) cout<<"NO" <<endl;
				else cout<<"YES"<<endl;
			continue;
		}
		bool flag = true;
		int l = n-k, r=  k ;
		if( l < k){
			for(int i = l ;i>=0 &&  i<n && i<r; ++i)
				if( s[i] != t[i]) flag = false;
		}
		sort( s.begin() , s.end()) ;
		sort( t.begin( ) ,t.end()) ;
		if( strcmp( s.c_str() ,t.c_str() ) != 0) flag = false;
		if( flag ) cout<<"YES"<<endl;
			else cout<<"NO" << endl ;
		
	} 
    return 0 ; 
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值