Educational Codeforces Round 187 解题报告
前情提要
刚调好了博客主题,感觉这个挺好看的。
省流:如果离散化不麻烦,那就不要想着去动态开点。

CF2203A
不说了。
CF2203B
额不难发现 \(s=F(s)\) 满足当且仅当 \(1 \le s \le 9\),直接把 \(x\) 每一位数字可以减少的大小全部塞到优先队列里(首位注意不要删成 \(0\),如果当前位已经是最小,就不要塞到队列里,否则浪费一次操作)。直接选最大的即可。
//to kill a living book
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--){
string s; cin>>s;
bool f=false; int sum=0;
priority_queue<int> q;
for(char ch:s){
if(!f){
f=true;
if(ch-'1'>0) q.push(ch-'1');
}
else{
if(ch-'0'>0) q.push(ch-'0');
}
sum+=ch-'0';
}
int cnt=0;
while(q.size()&&sum>=10){
sum-=q.top(),q.pop(),cnt++;
} cout<<cnt<<"\n";
}
return 0;
}
CF2203C
一开始读错题卡了二十分钟何意味?
其实第二个条件就代表二进制下数组里每一个数的二进制位为 \(1\) 的集合必须属于 \(m\) 二进制位为 \(1\) 的集合,那么考虑到低位权值总能整除高位,所以把数尽量安排在高位就行。可以直接二分答案钦定一个长度限制 \(len\),然后直接贪心,如果最后不够塞了那么 \(len\) 就是不合法的。好像要开 int128。
//to kill a living book
#include<bits/stdc++.h>
#define int long long
#define ll __int128
using namespace std;
int s,m;
bool chk(ll len){
ll sum=s;
for(ll i=63;i>=0;i--){
if(m&(1ll<<i)){
ll us=min(len,sum/(1ll<<i));
sum-=us*(1ll<<i);
}
}
return (sum==0);
}
void solve(){
cin>>s>>m;
ll l=0,r=1e18+1;
while(l+1<r){
ll mid=(l+r)/2;
if(chk(mid)) r=mid;
else l=mid;
}
if(!chk(r)) return cout<<"-1\n",void();
int res=r; cout<<res<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--) solve();
return 0;
}
CF2203D
看到一个笑话,Alice 和 Bob 的博弈其实是 BABA 博弈(好冷)。
考虑把 \(B\) 中的所有数分为三类:
- 能被 \(A\) 中所有数整除。
- 能被 \(A\) 中部分数整除。
- 不能被 \(A\) 中任何数整除。
令 \(vis_i\) 代表 \(i\) 可以被 \(A\) 中多少个数整除。这个直接调和级数预处理就行了,然后就可以 \(\mathcal{O}(1)\) 归类了。贪心很明显了,Alice 和 Bob 都要优先拿第二种数。看代码就行。
//to kill a living book
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int n,m,a[N],b[N],cc[4],vis[N<<1];
void solve(){
cin>>n>>m;
for(int i=1;i<=n+m;i++) vis[i]=0;
for(int i=1;i<=n;i++){
cin>>a[i],vis[a[i]]++;
}
for(int i=n+m;i>=1;i--){
for(int j=i+i;j<=n+m;j+=i) vis[j]+=vis[i];
}
cc[0]=cc[1]=cc[2]=0;
for(int i=1;i<=m;i++){
cin>>b[i];
if(vis[b[i]]==n) cc[2]++;
else if(!vis[b[i]]) cc[0]++;
else cc[1]++;
}
int nxt=0;
if(cc[1]&1) nxt=1-nxt;
while(1){
if(nxt==0&&cc[2]==0) return cout<<"Bob\n",void();
if(nxt==1&&cc[0]==0) return cout<<"Alice\n",void();
if(nxt==0) cc[2]--;
else cc[0]--;
nxt=1-nxt;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--) solve();
return 0;
}
下面放上我被 hack 的代码,真神秘。
//to kill a living book
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int n,m,a[N],b[N],cc[4],vis[N<<1],ok[N<<1];
void solve(){
cin>>n>>m;
int L=1;
for(int i=1;i<=n+m;i++) vis[i]=ok[i]=0;
for(int i=1;i<=n;i++){
cin>>a[i],L=lcm(L,a[i]);
vis[a[i]]=1;
}
for(int i=1;i<=n+m;i++){
if(vis[i]){
for(int j=i;j<=n+m;j+=i) ok[j]=1;
}
}
cc[0]=cc[1]=cc[2]=0;
for(int i=1;i<=m;i++){
cin>>b[i];
if(b[i]%L==0) cc[2]++;
else if(!ok[b[i]]) cc[0]++;
else cc[1]++;
}
int nxt=0;
if(cc[1]&1) nxt=1-nxt;
while(1){
if(nxt==0&&cc[2]==0) return cout<<"Bob\n",void();
if(nxt==1&&cc[0]==0) return cout<<"Alice\n",void();
if(nxt==0) cc[2]--;
else cc[0]--;
nxt=1-nxt;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--) solve();
return 0;
}
CF2203E
又是 BABA 博弈。
每一轮是独立的,单独考虑每一轮。
将 \(a[1..i]\) 排序后,推下式子,不难发现假设 Alice 选了 \(a\),那么 Bob 的最优决策肯定是 \(a\) 的前驱或后继中期望得分最大的那个,我们把这个值叫 \(f\)。而 Alice 的决策带来的得分 \(f\) 是关于下标呈严格山谷状的(可能山谷有平地)。而不难发现,选择 \(i\) 的前驱和后继带来的贡献分别随 \(i\) 的增加单调递增和递减。要找的那个最小值,可以直接三分,但是更聪明的方法是直接找前驱后继得分大小交界的位置。我们二分找到最后一个前驱得分小于后继得分的位置 \(pos\),然后枚举 \(pos\) 和 \(pos+1\),取 \(f\) 较小的那个就行。
计算得分的具体方式,我们举后继的例子,其实就是一段后缀和减去后缀的长度与 Bob 选择的值的乘积,这直接拿个权值线段树维护一下就可以了。但是我因为偷懒使用动态开点卡了一万年常没有过,加上 D 的奶龙错误被 hack,惨遭掉分。
//to kill a living book
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+7,mod=998244353;
int n,a[N],rt=1,tot,num[N];
int inv(int x){
int res=1;
for(int i=mod-2;i;i>>=1){
if(i&1) (res*=x)%=mod;
(x*=x)%=mod;
} return res;
}
struct sgt{
int s[N<<2],c[N<<2];
#define mid (l+r>>1)
#define ls (x<<1)
#define rs (x<<1|1)
void pu(int x){
c[x]=c[ls]+c[rs];
s[x]=s[ls]+s[rs];
}
void add(int x,int l,int r,int q){
if(l==r) return s[x]+=num[l],c[x]++,void();
if(q<=mid) add(ls,l,mid,q);
else add(rs,mid+1,r,q);
pu(x);
}
int cnt(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return c[x];
int res=0;
if(ql<=mid) res+=cnt(ls,l,mid,ql,qr);
if(mid<qr) res+=cnt(rs,mid+1,r,ql,qr);
return res;
}
int sum(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return s[x];
int res=0;
if(ql<=mid) res+=sum(ls,l,mid,ql,qr);
if(mid<qr) res+=sum(rs,mid+1,r,ql,qr);
return res;
}
int kth(int x,int l,int r,int k){
if(l==r) return l;
if(c[ls]>=k) return kth(ls,l,mid,k);
else return kth(rs,mid+1,r,k-c[ls]);
}
} Misaka;
int cl(int val,int i){
int res=0,cc,ss;
if(val!=1){
int pre=Misaka.kth(1,0,tot,val-1);
cc=Misaka.cnt(1,0,tot,1,pre-1);
ss=Misaka.sum(1,0,tot,1,pre-1);
res=max(res,num[pre]*cc-ss);
}
return res;
}
int cr(int val,int i){
int res=0,cc,ss;
if(val!=i){
int nxt=Misaka.kth(1,0,tot,val+1);
cc=Misaka.cnt(1,0,tot,nxt+1,tot);
ss=Misaka.sum(1,0,tot,nxt+1,tot);
res=max(res,ss-num[nxt]*cc);
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
set<int> s; map<int,int> mp;
for(int i=1;i<=n;i++) cin>>a[i],s.insert(a[i]);
for(int x:s) mp[x]=++tot,num[tot]=x;
for(int i=1;i<=n;i++) a[i]=mp[a[i]];
for(int i=1;i<=n;i++){
Misaka.add(rt,0,tot,a[i]);
if(i<3) continue;
int l=0,r=i+1;
while(l+1<r){
int m=(l+r)>>1;
if(cl(m,i)<=cr(m,i)) l=m;
else r=m;
}
int fl=1e18;
if(l>=1) fl=min(fl,max(cl(l,i),cr(l,i)));
if(l+1<=i) fl=min(fl,max(cl(l+1,i),cr(l+1,i)));
cout<<fl%mod*inv(i-2)%mod<<"\n";
}
return 0;
}
CF2203F
不想写。
注意到蒋老师使用了 52min 通过这题。
本文来自博客园,作者:GE9x,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/19644271

浙公网安备 33010602011771号