Codeforces Round #840 (Div. 2) and Enigma 2022 - Cybros LNMIIT
好久都没写博客了,今天vp了第一场CF,纪念一下。
A
先任意固定某两个数为最大值或最小值,再对每一位考虑,把每个数都扫一遍,如果既有 \(1\) 又有 \(0\),那么我们总可以把 \(1\) 换给最大值,把 \(0\) 换给最小值。那么答案的这一位就是 \(1\),否则是 \(0\)。
\(O(10n)\)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=643;
int a[N];
void solve(){
int n;read(n);
for(int i=1;i<=n;i++)read(a[i]);
int ans=0;
for(int i=0;i<=10;i++){
bool b1=0,b2=0;
for(int j=1;j<=n;j++){
if((a[j]>>i)&1)b1=1;
else b2=1;
}
if(b1&b2)ans+=(1<<i);
}
printf("%d\n",ans);
}
int main(){
int T;read(T);
while(T--)solve();
return 0;
}
B
似乎网上的方法都比我简单。。。
首先我们不关心怪兽的先后顺序,于是我们可以按怪兽的健康值排序,那么我们每次可以二分找到第一个大于 \(k\) 的位置 \(id\),那么它前面的都会被杀死,问题给到求 \([id,n]\) 的最小能力值,ST 表实现。
upd:后缀最小值只需要 \(O(n)\) 扫一遍,ST 表大材小用了。
\(O(n\log n)\)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e5+7,inf=1e9+7;
struct monster{
int h,p;
bool operator<(const monster &b)const{
return h<b.h;
}
}a[N];
int f[N][18],lg[N],n,k;
void st(){
for(int i=1;i<=n;i++)f[i][0]=a[i].p;
for(int j=1;j<=17;j++)for(int i=1;i+(1<<(j-1))<=n;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
lg[1]=0;lg[2]=1;
for(int i=3;i<=n;i++)lg[i]=lg[i/2]+1;
}
int query(int l,int r){
int t=lg[r-l+1];
return min(f[l][t],f[r-(1<<t)+1][t]);
}
void solve(){
read(n);read(k);
for(int i=1;i<=n;i++)read(a[i].h);
for(int i=1;i<=n;i++)read(a[i].p);
sort(a+1,a+n+1);st();
a[n+1].h=inf;
int now=1,sum=0;//注意我们没有减健康值,要记所有k的和
while(k>0){
int t=upper_bound(a+now,a+n+2,monster{sum+k,0})-a;
now=t;
if(now>n)break;
sum+=k;
k-=query(now,n);
}
puts(now>n?"Yes":"No");
}
int main(){
int T;read(T);
while(T--)solve();
return 0;
}
C
结论:记 \(mx\) 为整个序列的最大值,那么当 \(n\ge 4\) 时整个序列都能变成 \(mx\),答案为 \(mx\times n\)。
证明:记 \(k\) 为 \(mx\) 在序列中的位置。
显然如果对一个 \([l,r]\) 操作两次就可以使其全变成 \(0\),利用好这个性质。
当 \(k=1\) 时,操作:\([2,n]\times 2\),\([1,n]\)。
当 \(k=2\) 时,操作:\([3,n]\times 2\),\([2,n]\),\([1,2]\times 2\),\([1,n]\)。
当 \(3\le k \le n-2\) 时,操作:\([1,k-1]\times 2\),\([k+1,n]\times 2\),\([k,n]\),\([1,n]\)。
当 \(k=n-1\) 时,操作:\([1,n-2]\times 2\),\([1,n-1]\),\([n-1,n]\times 2\),\([1,n]\)。
当 \(k=n\) 时,操作:\([1,n-1]\times 2\),\([1,n]\)。
证毕。
再次注意只针对于 \(n\ge 4\)。\(n=2\) 或 \(3\) 手模即可。
\(O(n)\)。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=2e5+7;
int a[N];
void solve(){
int n,mx=0;read(n);
for(int i=1;i<=n;i++)read(a[i]),mx=max(mx,a[i]);
if(n>3)printf("%lld\n",n*mx);
else if(n==2)printf("%lld\n",max(a[1]+a[2],2*abs(a[1]-a[2])));
else if(n==3)printf("%lld\n",max(a[1]+a[2]+a[3],3*max({a[1],a[3],abs(a[1]-a[2]),abs(a[1]-a[3]),abs(a[2]-a[3])})));
}
main(){
int T;read(T);
while(T--)solve();
return 0;
}
D 翻译没看懂,刚好要上网课,就没继续做了。
DE似乎不是很难,在我能力范围内,如果我记得就补。