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';
        }
    }
}
posted @ 2024-10-08 07:04  HaneDaniko  阅读(100)  评论(0编辑  收藏  举报