【Codeforces Round #544 (Div. 3)】 A.B.C.D.E.F1.F2.

本文总结了一次Codeforces比赛的经历,详细解析了六道题目的算法思路,包括时间计算、配对求和、团队平衡、零数量最大化、团队平衡进阶及生成树最大度数问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

这场由于大号都不算分,于是开新号打,开场还算顺利,但是由于D题的坑点较多,有点慌,导致E题也开始乱提交,最后没时间做F2。打div3打成这样是不应该的。

biu-biubiu rating+=168rating+=168rating+=168 1500->1668


A. Middle of the Contest

题意

输出两个时间的中间时间。

做法

由于两个时间在同一天,直接转换成分钟取平均值就可以。

代码

#include<stdio.h>
int main()
{
    int h1,m1,h2,m2;
    scanf("%d:%d",&h1,&m1);
    scanf("%d:%d",&h2,&m2);
    printf("%02d:%02d\n",(h1*30+h2*30+(m1+m2)/2)/60,(h1*30+h2*30+(m1+m2)/2)%60);
    return 0;
}


B. Preparation for International Women’s Day

题意

给你n个数,两个数成对取出,求最多可以取出多少对和是k的倍数。

1≤n≤2×1051 \leq n \leq 2 \times 10^51n2×105
1≤k≤1001 \leq k \leq 1001k100

做法

对于每个数只保存对k取模之后的余数即可,之后对于每个余数与对应的余数匹配即可。
要特判000和余数等于k/2k/2k/2的时候。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 2e5+5;
int d[maxn];
int sum[maxn];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d[i]);
        sum[d[i]%k]++;
    }
    int ans=sum[0]/2;
    for(int i=1;i<=k/2;i++)
    {
        if(i==k-i) ans=ans+sum[i]/2;
        else ans=ans+min(sum[i],sum[k-i]);
    }
    printf("%d\n",ans*2);
    return 0;
}


C. Balanced Team

题意

给你nnn个数字,从中选出尽量多的数字,使这些数中最大值与最小值的差不超过555.

做法

首先将数组排序,二分找到每个值能向右伸展的最长长度即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 2e5+5;
int a[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    int ans=1;
    for(int i=1;i<=n;i++)
    {
        int pos=upper_bound(a+1,a+1+n,a[i]+5)-a;
        ans=max(ans,pos-i);
    }
    printf("%d\n",ans);
    return 0;
}


D. Zero Quantity Maximization

题意

给你两个长度为nnn的数组aaa,bbbci=ai×d+bic_i = a_i \times d +b_ici=ai×d+bi,现在找到一个ddd使尽量多的cic_ici000
1≤n≤1051 \leq n \leq 10^51n105
−109≤ai,bi≤109-10^9 \leq a_i,b_i \leq10^9109ai,bi109
做法

ci=0c_i=0ci=0,那么d=−biaid=-\frac{b_i}{a_i}d=aibi,只需要看哪个d出现次数最多即可,这里我怕被卡精度,于是存分数约分后的最简形式即可,这里要注意ai=0,bi=0a_i=0,b_i=0ai=0,bi=0等情况。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 2e5+5;
map<pii,int> mp;
int a[maxn],b[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    int ans=0;
    int tmp=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==0&&b[i]==0)
        {
            tmp++;
            continue;
        }
        else if(a[i]==0) continue;
        if(b[i]==0)
        {
            mp[pii(0,1)]++;
            ans=max(ans,mp[pii(0,1)]);
            continue;
        }
        int up=-b[i]/__gcd(a[i],b[i]);
        int down=a[i]/__gcd(a[i],b[i]);
        if(up<0)
        {
            up=up*-1;
            down=down*-1;
        }
        mp[pii(up,down)]++;
        ans=max(ans,mp[pii(up,down)]);
    }
    printf("%d\n",ans+tmp);
    return 0;
}


E. K Balanced Teams

题意

给你n个数,分成k组,要求每组内最大值与最小值的差值不超过5。求k组最多可以放多少个数。

1≤n,k≤50001 \leq n,k \leq 50001n,k5000
做法

首先对数组排序,我们可以预处理每个数最多可以向左扩展的长度。
之后我们用dp[i][j]dp[i][j]dp[i][j]表示前i个数分为j组最多可以放多少个数。
对于每个i,一定是从之前预处理的位置转移过来,因为一段应该放尽量多的值。
我们设位置iii能够扩展的最左位置为l[i]l[i]l[i]。那么转移方程为:
dp[i][j]=max(dp[i−1][j],dp[i][j−1],dp[l[i]−1][j−1]+i−l[i]+1)dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[l[i]-1][j-1]+i-l[i]+1)dp[i][j]=max(dp[i1][j],dp[i][j1],dp[l[i]1][j1]+il[i]+1)
最后dp[n][k]dp[n][k]dp[n][k]便是答案。
代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 5005;
int a[maxn],l[maxn];
int dp[maxn][maxn];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    dp[0][0]=0;
    for(int i=1;i<=n;i++) l[i]=lower_bound(a+1,a+1+n,a[i]-5)-a;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            dp[i][j]=max(dp[i][j],dp[l[i]-1][j-1]+i-l[i]+1);
        }
    }
    printf("%d\n",dp[n][k]);
    return 0;
}


F1. Spanning Tree with Maximum Degree

题意

给你nnn个点mmm条边的无向联通图,找出一棵生成树,使度最大的点的度最大。

1≤n,m≤1051 \leq n,m \leq 10^51n,m105
做法

首先度最大的点一定等于原图中度最大的点的度,那么我们只需要找到原图中度最大的点,由这个点向外bfsbfsbfs即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 2e5+5;
#define Se second
#define Fi first
#define pb push_back
int vis[maxn];
vector<int>G[maxn];
vector<pii> ans;
int in[maxn];
void bfs(int rt)
{
    vis[rt]=1;
    queue<int> q;
    q.push(rt);
    while(!q.empty())
    {
        int tp=q.front();
        q.pop();
        for(int i=0;i<G[tp].size();i++)
        {
            int to=G[tp][i];
            if(vis[to]) continue;
            vis[to]=1;
            ans.pb(pii(tp,to));
            q.push(to);
        }
    }
}
int main()
{
    int u,v,n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        G[u].pb(v);
        G[v].pb(u);
        in[u]++;
        in[v]++;
    }
    int maxx=0;
    int pos=0;
    for(int i=1;i<=n;i++)
    {
        if(in[i]>maxx)
        {
            maxx=in[i];
            pos=i;
        }
    }
    bfs(pos);
    for(int i=0;i<ans.size();i++) printf("%d %d\n",ans[i].Fi,ans[i].Se);
    return 0;
}


F2. Spanning Tree with One Fixed Degree

题意

给你nnn个点mmm条边的无向联通图,找出一棵生成树,使111这个点的度=d=d=d

1≤n,m≤1051 \leq n,m \leq 10^51n,m105
做法

首先我们把111这个点先拿出来,如果111的度最初就小于ddd,答案一定不存在,否则对除111这个点之外剩下的图求连通分量,并记录每个连通分量之内有哪些点与111相连,之后为保证联通,每个连通分量一定至少和111连一条边,之后如果此时111的度已经超过ddd,则答案不存在,否则将多余的度与那些之前没连过而且与111有边的点相连即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<string.h>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 2e5+5;
#define Se second
#define Fi first
#define pb push_back
vector<int>G[maxn];
vector<int>dsu[maxn];
vector<int>one[maxn];
vector<pii> ans;
int n,m,d,in[maxn];
int vis[maxn];
int tot;
void dfs(int x)
{
    dsu[tot].pb(x);
    vis[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int to=G[x][i];
        if(vis[to]) continue;
        if(to==1)
        {
            one[tot].pb(x);
            continue;
        }
        dfs(to);
    }
}
int main()
{
    int u,v;
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        in[u]++;
        in[v]++;
        G[u].pb(v);
        G[v].pb(u);
    }
    if(in[1]<d)  return 0*puts("NO");
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            ++tot;
            dfs(i);
        }
    }
    if(tot>d) return 0*puts("NO");
    memset(vis,0,sizeof(vis));
    queue<int> q;
    vis[1]=1;
    for(int i=1;i<=tot;i++)
    {
        int b=one[i].back();
        ans.pb(pii(1,b));
        vis[b]=1;
        q.push(b);
        one[i].pop_back();
        d--;
    }
    for(int i=1;i<=tot;i++)
    {
        while(d>0&&one[i].size()>0)
        {
            int b=one[i].back();
            ans.pb(pii(1,b));
            vis[b]=1;
            q.push(b);
            one[i].pop_back();
            d--;
        }
    }
    while(!q.empty())
    {
        int tp=q.front();
        q.pop();
        for(int i=0;i<G[tp].size();i++)
        {
            int to=G[tp][i];
            if(vis[to]) continue;
            ans.pb(pii(tp,to));
            vis[to]=1;
            q.push(to);
        }
    }
    puts("YES");
    for(int i=0;i<ans.size();i++) printf("%d %d\n",ans[i].Fi,ans[i].Se);
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值