Codeforces Round 977 (Div. 2, based on COMPFEST 16 - Final Round)
致敬传奇调题王 HDK
本场最难崩评论
A.Meaning Mean
给定一个序列 \(a\),每次选择 \(i,j\ (i\neq j)\),使得其缩成一个值为 \(\lfloor\frac{a_i+a_j}{2}\rfloor\) 的数,直至剩余一个数,求最终答案的最大值
一开始想的是最小化 \(\lfloor\frac{a_i+a_j}{2}\rfloor\) 的损失,后来发现这点损失基本上可以不管,因为最后的值一定可以表示成 \(\lfloor\frac{a_i+\lfloor\frac{a_j+\cdots}{2}\rfloor}{2}\rfloor\) 的形式,可以近似看成 \(a_i\times 2^{-1}+a_j\times 2^{-2}\times\cdots\),最大化其值也即将最大值放在前面,贪心即可
#define int long long
int n;
int a[100001];
bool vis[100001];
signed main(){
int cases;cin>>cases;while(cases--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
vis[i]=false;
}
int cnt=0;
sort(a+1,a+n+1);
int ans=(a[1]+a[2])/2;
for(int i=3;i<=n;++i){
ans=(ans+a[i])/2;
}
cout<<ans<<'\n';
}
}
B.Maximize Mex
给定一个序列 \(a\) 与一个数 \(x\),可以进行如下操作
- 选择任意一个 \(i\),使得 \(a_i\) 自增 \(x\)
最大化其 \(\text{mex}\)
发现一个 \(a_i\) 只能由满足 \(a_j\equiv a_i\pmod x\) 且 \(a_j\le a_i\) 的 \(j\) 转移得到
因此对 \(a_i\mod x\) 开桶,求 \(\text{mex}\) 肯定是枚举自然数,\(a_j\le a_i\) 的条件可以通过每次转移完再放进桶里来实现,每次从桶里拿一个出来填 \(\text{mex}\)
#define int long long
int n,x;
int a[200001];
int cnt[200001];
int remaincnt[200001];
signed main(){
int cases;cin>>cases;while(cases--){
cin>>n>>x;
memset(cnt,0,sizeof cnt);
memset(remaincnt,0,sizeof remaincnt);
for(int i=1;i<=n;++i){
cin>>a[i];
if(a[i]>n) continue;
cnt[a[i]]++;
}
bool flag=false;
for(int i=0;i<=n;++i){
if(cnt[i]==0){
if(remaincnt[i%x]==0){
cout<<i<<'\n';
flag=true;
break;
}
remaincnt[i%x]--;
}
else{
cnt[i]--;
}
remaincnt[i%x]+=cnt[i];
}
}
}
C2.Adjust The Presentation (Hard Version)
发现问题可以转化成 “\(a\) 中的元素在 \(b\) 中第一次出现的位置是否升序排列”,因为你会发现元素只有第一次出现的位置有关系,如果一个元素已经出现过一次,那么总会能够灵活地插入到它下一次应该出现的位置上,这也就意味着,如果一个元素已经出现过,那么它就与答案无关了
所以一个线性的 check 就出来了,统计 \(a\) 中每个元素在 \(b\) 中第一次出现的位置,判断是否升序排列
然而复杂度不够优秀,考虑加入一个 \(cnt\) 统计当前有多少不满足升序排列的数对,显然 \(cnt=0\) 时合法
对值域上每一个元素开一个 set,记录其所有出现的位置,这样插入与删除位置,查询最小值都是 \(\log\) 的,我们就可以快速维护元素第一次出现的位置
然后让 \(cnt\) 减去修改前,修改位置旁边的不合法对数,加上修改后的对数,只统计修改位置旁边的是因为只有这些值可能会改变
需要注意的
- 特判修改位置为 \(1\) 或 \(n\) 的情况
- 如果被修改位置和目标位置在值域上离得太近,比如 \(A-B=1\),那么要留神是不是将 \((A,B)\) 和 \((B,A)\) 全都统计进答案里了,然而这是错误的
#define int long long
int n,m,q;
int a[200001],b[200001];
int pos[200001];
set<int>o_pos[200001];
int unsucc_cnt=0;
void check(){
for(int i=1;i<=n-1;++i){
if(*o_pos[a[i]].begin()>=*o_pos[a[i+1]].begin()) unsucc_cnt++;
}
}
signed main(){
ios::sync_with_stdio(false);
int cases;cin>>cases;while(cases--){
cin>>n>>m>>q;
unsucc_cnt=0;
for(int i=1;i<=n;++i){
cin>>a[i];
pos[a[i]]=i;
o_pos[i].clear();
}
for(int i=1;i<=m;++i){
cin>>b[i];
o_pos[b[i]].insert(i);
}
for(int i=1;i<=n;++i){
o_pos[a[i]].insert(m+n+pos[a[i]]);
}
check();
cout<<(unsucc_cnt==0?"YA":"TIDAK")<<'\n';
while(q--){
int x,y;
cin>>x>>y;
int uncnt1=0,uncnt2=0;
if(pos[b[x]]!=1 and *o_pos[a[pos[b[x]]-1]].begin()>=*o_pos[b[x]].begin()) uncnt1++;
if(pos[b[x]]!=n and *o_pos[b[x]].begin()>=*o_pos[a[pos[b[x]]+1]].begin()) uncnt1++;
if(pos[y]!=1 and a[pos[y]-1]!=b[x] and *o_pos[a[pos[y]-1]].begin()>=*o_pos[y].begin()) uncnt1++;
if(pos[y]!=n and a[pos[y]+1]!=b[x] and *o_pos[y].begin()>=*o_pos[a[pos[y]+1]].begin()) uncnt1++;
o_pos[b[x]].erase(x);
o_pos[y].insert(x);
if(pos[b[x]]!=1 and *o_pos[a[pos[b[x]]-1]].begin()>=*o_pos[b[x]].begin()) uncnt2++;
if(pos[b[x]]!=n and *o_pos[b[x]].begin()>=*o_pos[a[pos[b[x]]+1]].begin()) uncnt2++;
if(pos[y]!=1 and a[pos[y]-1]!=b[x] and *o_pos[a[pos[y]-1]].begin()>=*o_pos[y].begin()) uncnt2++;
if(pos[y]!=n and a[pos[y]+1]!=b[x] and *o_pos[y].begin()>=*o_pos[a[pos[y]+1]].begin()) uncnt2++;
b[x]=y;
unsucc_cnt+=uncnt2-uncnt1;
cout<<(unsucc_cnt==0?"YA":"TIDAK")<<'\n';
}
}
}