Codeforces Round #629(Div.3)题解

题目链接:

文章目录

    • A:Divisibility Problem
    • B:K-th Beautiful String
    • C: Ternary XOR
    • D:Carousel
    • E:Tree Queries
    • F:Make k Equal

A:Divisibility Problem

大致题意:
给你两个数 a,b,一种操作:a=a+1;求最少操作次数使得a%b==0
解题报告:
签到题

#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e6+5;
const int MOD=998244353;
const int inf=0x3f3f3f3f;
const double PI=acos(-1);
const double e=2.718281828459;
 
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        if(n%m==0){ printf("0\n"); }
        else{ printf("%d\n",m-n%m); }
    }
    return 0;
}

B:K-th Beautiful String

大致题意:
给你整数 n,用 n-2 个’a’ 和 2个 ’b’ 构成构成字符串,一共可构成 n*(n-1)/2个,再将他们按字典序进行升序排列,问你第 k个是哪个字符串。
解题报告:
简单模拟找规律:
首先将a排成一串,那么第一个b就有n-1种插入法,又因按字典序升序排列,显然b越靠右越小,当插入一个b后,第二个b就有(第一个b之后有多少个a+1)种插法。
发现当确定第一个b位置时可排列的规律:1,2,3,4…
首先二分确定第一个b的位置,在推出第二个位置即可。

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e6+5;
const int MOD=998244353;
const int inf=0x3f3f3f3f;
const double PI=acos(-1);
const double e=2.718281828459;
 
int main()
{
    int T;
    ll n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        ll l=1,r=1e5,ans;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(mid*(mid+1)/2<=m){
                ans=mid;l=mid+1;
            }else{r=mid-1;}
        }
        if(ans*(ans+1)/2==m){
            for(int i=1;i<=n-ans-1;i++){printf("a");}
            printf("bb");
            for(int i=1;i<=ans-1;i++){printf("a");}
            printf("\n");continue;
        }
        ll pp=m-(ans+1)*ans/2;
        for(int i=1;i<=n-ans-2;i++){printf("a");}
        printf("b");
        for(int i=1;i<=ans-(pp-1);i++){printf("a");}
        printf("b");
        for(int i=1;i<=pp-1;i++){printf("a");}
        printf("\n");
    }
    return 0;
}

C: Ternary XOR

大致题意:
一种操作:
两个字符串 a b 仅由 0 1 2 构成,c 字符串 = a ** b
** :c_i = ( a_i + b_i )%3
现在给你字符串 c 要你确定 字符串 a b,并且满足max( a, b) 尽量小
解题报告:
我们发现只有第一次出现 1 时才会开始区分 a b 的大小 ,v1记录1 v2记录0,那么对于之前的 0 2 都是均分,对于之后的 2 1 都分配给 v2,v1分配0
保证了max( a , b ) 最小

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MOD=1e9+7;
const int maxn=1e6+5;
const double eps=1e-6;
const int inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double e=2.718281828459;
 
char s[maxn];
vector<int>v1,v2;

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--){
        v1.clear();v2.clear();
        scanf("%d%s",&n,s+1);
        bool flag=false;
        for(int i=1;i<=n;i++){
            if(s[i]=='2'){
                if(flag==false){v1.pb(1);v2.pb(1);}
                else{v1.pb(0);v2.pb(2);}
            }
            else if(s[i]=='0'){v1.pb(0);v2.pb(0);}
            else{
                if(flag==false){v1.pb(1);v2.pb(0);flag=true;}
                else{v1.pb(0);v2.pb(1);}
            }
        }
        for(auto i:v1){printf("%d",i);}
        printf("\n");
        for(auto i:v2){printf("%d",i);}
        printf("\n");
    }
    return 0;
}

D:Carousel

大致题意:
给你n个数,成环状排列即 n 与1 相邻。现在叫你将他们染色,但有个要求相邻的不相等数必须染成不同颜色,并且所用颜色数尽量少。
解题报告:
答案只有 1 2 3 三种
1:只出现一种数
2:n为偶数 或者 n为奇数且(细节看代码更容易理解)
3:n为奇数且(细节看代码)

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
 
 
const int maxn=2e5+5;
const int MOD=998244353;
const int inf=0x3f3f3f3f;
const double PI=acos(-1);
const double e=2.718281828459;
 
 
int q[maxn],p[maxn];
 
int main()
{
    int n,T;
    scanf("%d",&T);
    while(T--){
        bool flag=false,fl=false;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&q[i]);
        for(int i=2;i<=n;i++){
            if(q[i]!=q[i-1]){flag=true;}
            else fl=true;
        }
        if(!flag){
            printf("1\n");
            for(int i=1;i<=n;i++) printf("1 ");
            printf("\n");continue;
        }
        if(n%2==0){
            printf("2\n");
            for(int i=1;i<=n;i++){
                if(i&1) printf("1 ");
                else printf("2 ");
            }
            printf("\n");continue;
        }
        p[1]=1;
        if(q[n-1]==q[1]){
            if(fl) p[n]=2,printf("2\n"),flag=false;
            else p[n]=3,printf("3\n");
        }
        else{
            if(q[n]==q[1]) p[n]=1,printf("2\n");
            else if(q[n]==q[n-1])p[n]=2,printf("2\n");
            else {
                if(fl) p[n]=2,printf("2\n"),flag=false;
                else p[n]=3,printf("3\n");
            }
        }
        for(int i=2;i<n;i++){
            if(!flag&&q[i]==q[i-1]){
                flag=true,p[i]=p[i-1];continue;
            }
            if(p[i-1]==2) p[i]=1;
            else p[i]=2;
        }
        for(int i=1;i<=n;i++) printf("%d ",p[i]);
        printf("\n");
    }
    return 0;
}

E:Tree Queries

大致题意:
给你一颗树 n 个点 n-1 条边,默认1 为根节点。问是否能找到一个节点满足所有询问的节点或者其父节点在根节点到其路径上。
解题报告:
前缀知识要知道LCA。
很明显那个节点(mx)一定是所有询问节点中深度最深的,再通过mx与其他节点判断 LCA ,并且是否满足LCA到其他节点的距离 <=1。

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
 
 
const int maxn=2e5+5;
const int MOD=998244353;
const int inf=0x3f3f3f3f;
const double PI=acos(-1);
const double e=2.718281828459;
 
 
pii p[maxn<<1];
int deep[maxn],f[maxn][25],cnt,head[maxn<<1],q[maxn];
 
void add(int from,int to)
{
    p[++cnt].s=head[from];
    p[cnt].f=to;
    head[from]=cnt;
}
void dfs(int now,int pre)
{
    deep[now]=deep[pre]+1;
    f[now][0]=pre;
    for(int i=1;i<=20;i++){
        f[now][i]=f[f[now][i-1]][i-1];
    }
    for(int i=head[now];~i;i=p[i].s){
        int to=p[i].f;
        if(to==pre){continue;}
        dfs(to,now);
    }
}
int LCA(int x,int y)
{
    if(deep[x]<deep[y]){swap(x,y);}
    for(int i=20;i>=0;i--){
        if(deep[f[x][i]]>=deep[y]){x=f[x][i];}
    }
    if(x==y){return x;}
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];y=f[y][i];
        }
    }
    return f[x][0];
}
 
int main()
{
    mem(head,-1);
    int n,m,u,v,mx,k;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0);
    while(m--){
        mx=0;
        bool flag=false;
        scanf("%d",&k);
        for(int i=1;i<=k;i++){
            scanf("%d",&q[i]);
            if(deep[mx]<deep[q[i]]){mx=q[i];}
        }
        for(int i=1,j;i<=k;i++){
            if(mx==q[i]){continue;}
            j=LCA(q[i],mx);
            if(deep[q[i]]-deep[j]>1){flag=true;break;}
        }
        if(!flag){printf("YES\n");}
        else{printf("NO\n");}
    }
    return 0;
}

F:Make k Equal

大致题意:
给你一组数,求最小操作次数使得该组数中存在至少 K 个相等的数
每一次操作:你可以选择最小的数让其+1或者选择最大的数让其-1
解题报告:
显然最终最少k个相等的数在原数组中也存在,
不妨枚举与数组中出现的每种数,对于当前数(A)考虑要么将比他小的数转变成 A-1,要么将比他大的数转变成A+1,再次操作它们使得构成k个相等的数A

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include 
#define pii pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;


const int MOD=1e9+7;
const int maxn=2e5+5;
const double eps=1e-6;
const int inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double e=2.718281828459;


int p[maxn],num[maxn],a[maxn];
ll pre1[maxn],pre2[maxn];

int main()
{
    int n,k,cnt=0;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(a[i]==a[i-1]){
            num[cnt]++;
        }
        else{
            p[++cnt]=a[i];
            num[cnt]=1;
        }
    }
    for(int i=1;i<=cnt;i++){
        pre1[i]=pre1[i-1]+num[i];
        pre2[i]=pre2[i-1]+p[i]*num[i];
    }
    ll ans=1e18,res;
    for(int i=1;i<=cnt;i++){
        if(num[i]>=k){
            ans=0;break;
        }
        res=1e18;
        ll sk1=1ll*(p[i]-1)*pre1[i-1]-pre2[i-1];
        ll sk2=1ll*(pre2[cnt]-pre2[i])-1ll*(p[i]+1)*(pre1[cnt]-pre1[i]);
        if(pre1[i]>=k){
            res=min(res,sk1+(k-num[i]));
        }
        else{
            res=min(res,sk1+pre1[i-1]+sk2+(k-pre1[i]));
        }
        if(pre1[cnt]-pre1[i-1]>=k){
            res=min(res,sk2+k-num[i]);
        }
        else{
            res=min(res,sk2+pre1[cnt]-pre1[i]+sk1+k-pre1[cnt]+pre1[i-1]);
        }
        ans=min(ans,res);
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(codeforces比赛题解)