Codeforces Round #629 (Div. 3)【ABCDEF】(题解)

目录

涵盖知识点:思维、构造、树上倍增。

比赛链接:传送门

博客园目录好像不能用toc了???容我研究一下。。。

A - Divisibility Problem

题意: 给两个数\(a,b\),每次操作可以使\(a=a+1\),问最少几次操作后\(a\)\(b\)的倍数。
题解:

\[ans=\begin{cases} 0\qquad a \% b=0 \\ b-a \qquad a<=b \\ b-(a\% b)\qquad a>b \end{cases} \]

Accept Code:

#include <bits/stdc++.h>
using namespace std;

int main(){
    int t;
    cin>>t;
    while(t--){
        int a,b;
        cin>>a>>b;
        if(a%b==0){cout<<"0\n";continue;}
        if(a<=b)cout<<b-a<<"\n";
        else cout<<b-(a%b)<<"\n";
    }
    return 0;
}

B - K-th Beautiful String

题意: 长度为\(n\)的字符串包含\(n-2\)个a和\(2\)个b,求按照字典序排列的第\(k\)个。
题解: 观察样例,左边的b的位置出现次数按照\(1,2,3\ldots\)排列,确定后再确定右边的b的出现位置即可。
Accept Code:

#include <bits/stdc++.h>
using namespace std;

int main(){
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        int l=1;
        while(k>l)k-=l,l++;
        l++;
        int r=k;
        for(int i=n;i>=1;i--){
            if(i==l||i==r)cout<<"b";
            else cout<<"a";
        }
        cout<<"\n";
    }
    return 0;
}

C - Ternary XOR

题意: 规定三进制下的运算\(c = a \odot b\)\(c_i = (a_i + b_i) \% 3\),现给定\(c\),要求构造\(a,b\),并使得\(max(a,b)\)尽可能小。
题解: 第一个1分配给\(a\),后面的所有数字一律分配给\(b\)
Accept Code:

#include <bits/stdc++.h>
using namespace std;

int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        string s,a,b;
        cin>>s;
        bool flag=true;
        for(char i : s){
            if(flag) {
                if (i == '0')a += '0', b += '0';
                if (i == '1')a += '1', b += '0',flag=false;
                if (i == '2')a += '1', b += '1';
            }else a+='0',b+=i;
        }
        cout<<a<<"\n"<<b<<"\n";
    }
    return 0;
}

题意: \(n\)个动物围成一个环,现在要给动物上色,要求不能给相邻的不同动物上同一种颜色,问最少几种颜色可以满足条件。
题解:

  1. 全同色,1种
  2. 不存在相邻的相同动物且为奇数,3种。(前面1,2间隔最后一个3)
  3. 其余情况两种。偶数(12121212)奇数(找组相邻相同的把12反向)

画个图脑补一下就好了。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        a[0]=a[n];
        bool flag=true;
        for(int i=1;i<=n;i++){
            if(a[i]!=a[i-1]){
                flag=false;
                break;
            }
        }
        if(flag){
            cout<<"1\n";
            for(int i=1;i<=n;i++)cout<<1<<" ";
            cout<<"\n";
            continue;
        }
        int pos=0;
        for(int i=1;i<=n;i++){
            if(a[i]==a[i-1])
                pos=i;
        }
        if(!pos&&(n&1)){
            cout<<"3\n";
            for(int i=1;i<n;i++)cout<<i%2+1<<" ";
            cout<<"3\n";
        }
        else{
            cout<<"2\n";
            if(!(n&1)){
                for(int i=1;i<=n;i++)cout<<i%2+1<<" ";
                cout<<"\n";
            }else{
                for(int i=1;i<pos;i++)cout<<i%2+1<<" ";
                for(int i=pos;i<=n;i++)cout<<2-i%2<<" ";
                cout<<"\n";
            }
        }
    }
    return 0;
}

E - Tree Queries

题意: 给定一颗1为根的\(n\)顶点根树。对于\(m\)个询问,每个询问给\(k\)个点,问是否存在1出发的一条链使得这\(k\)个点距离链的距离小于1。
题解:\(k\)个点里找深度最大的点为该链终点。扫描一下其他的点即可。暴力肯定不能过,倍增优化一下跳跃步长就行了。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int maxd=20;
vector<int> edg[maxn];

int fa[maxn][maxd];
int deg[maxn];
int vi[maxn];
void bfs(int root){
    queue<int> q;
    deg[root]=0;
    fa[root][0]=root;
    q.push(root);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=1;i<maxd;i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        for(auto v:edg[u]){
            if(v==fa[u][0])continue;
            deg[v]=deg[u]+1;
            fa[v][0]=u;
            q.push(v);
        }
    }
}
int jump(int u,int det){
    for(int i=0;det;det>>=1,i++){
        if(det&1)u=fa[u][i];
    }
    return u;
}
int main(){
    int t;
    //cin>>t;
    //while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=1,u,v;i<n;i++){
            cin>>u>>v;
            edg[u].push_back(v);
            edg[v].push_back(u);
        }
        bfs(1);
        while(m--){
            int k;
            cin>>k;
            for(int i=0;i<k;i++)cin>>vi[i];
            int x=1;
            for(int i=0;i<k;i++){
                if(deg[fa[vi[i]][0]]>deg[x])x=fa[vi[i]][0];
            }
            //cout<<"x:"<<x<<"\n";
            bool flag=true;
            for(int i=0;i<k;i++){
                if(jump(x,deg[x]-deg[fa[vi[i]][0]])!=fa[vi[i]][0]){
                    flag=false;
                    break;
                }
            }
            puts(flag?"YES":"NO");
        }
    //}
    return 0;
}

F - Make k Equal

题意: \(n\)个数的数组\(a\),每次操作可以将某个最大值-1或者某个最小值+1.问操作几次使得至少存在\(k\)个相等的数字。
题解: 我们假设\(k\)个相等的数字都是\(i\),只有先将所有小于\(i\)的数字变为\(i-1\)或者所有大于\(i\)的数字变为\(i+1\)才能够操作成\(i\)。所以我们只要把\(a\)排序后维护一个前缀和和一个后缀和。计算出前缀变成\(i-1\)和后缀变成\(i+1\)的次数就可以简单的计算出答案。扫描一遍取最小值即可。细节看代码(最大值会超过0x3f3f3f3f。。。挂了一发)
Accept Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
int a[maxn];
set<ll> st;
map<ll,ll> mp;
int main() {
    int n,k;
    cin>>n>>k;
    ll suml=0,sumr=0,cntl=0,cntr=n;
    for(int i=0;i<n;i++)cin>>a[i],sumr+=a[i],st.insert(a[i]),mp[a[i]]++;
    sort(a,a+n);
    ll res=inf;
    for(auto i:st){
        if(mp[i]>=k){
            cout<<"0\n";
            return 0;
        }
        sumr-=mp[i]*i;
        cntr-=mp[i];
        ll cnt=k-mp[i];
        ll l=cntl*(i-1)-suml,r=sumr-cntr*(i+1);
        if(cntl>=cnt)res=min(res,l+cnt);
        if(cntr>=cnt)res=min(res,r+cnt);
        res=min(res,l+r+cnt);
        suml+=mp[i]*i;
        cntl+=mp[i];
    }
    cout<<res<<"\n";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/charles1999/p/12581503.html