题目一: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 ;
}
大意:一个数组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 ;
}
大意:一个轮盘有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 ;
}