AtCoder Beginner Contest 214 题解(A-E)

AtCoder Beginner Contest 214 题解(A-E)

A. New Generation ABC

题目大意:

根据数的范围输出答案。

解题思路:

签到题。

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
    cin>>n;
    if(n<=125) puts("4");
    else if(n<=211) puts("6");
    else puts("8");       
    return 0;
}

B. How many?

题目大意:

问有多少个三元组 ( a , b , c ) (a,b,c) (a,b,c)满足 a + b + c ≤ S , a × b × c ≤ T a+b+c\le S,a\times b\times c\le T a+b+cS,a×b×cT

解题思路:

因为数据比较小,直接 O ( S 3 ) O(S^3) O(S3)暴力就行。

代码:

#include<bits/stdc++.h>
using namespace std;
int S,T;
int main()
{
    cin>>S>>T;
    int res=0;
    for(int i=0;i<=S;i++)
        for(int j=0;j+i<=S;j++)
            for(int k=0;k+i+j<=S;k++)
                    if(i*j*k<=T) res++;

    cout<<res<<endl;
    return 0;
}

C. Distribution

题目大意:

n n n个人站成一个圈,第 i i i个人会在 t i t_i ti的时间得到一颗宝石,并且在 s i s_i si秒后将石头递给下一个人,问每个人第一次得到的宝石的时间是多少。

解题思路:

每个人只能从前一个人手上得到宝石或者到了 t i t_i ti时间得到宝石。

所以我们就从最早得到宝石的那个人枚举一圈, a n s [ i ] = min ⁡ ( t [ i ] , a n s [ i − 1 ] + s [ i − 1 ] ) ans[i]=\min(t[i],ans[i-1]+s[i-1]) ans[i]=min(t[i],ans[i1]+s[i1])

时间复杂度 O ( n ) O(n) O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10;
LL a[N],s[N],t[N];
int n;
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%lld",&s[i]);
    for(int i=0;i<n;i++) scanf("%lld",&t[i]);
    int p=0,Min=2e9;
    for(int i=0;i<n;i++)
        if(t[i]<Min){
            p=i;
            Min=t[i];
        }
    a[p]=t[p];
    for(int i=1;i<n;i++){
        int cur=(p+i)%n,pre=(p+i-1+n)%n;
        a[cur]=min(t[cur],a[pre]+s[pre]);
    }
    for(int i=0;i<n;i++) printf("%lld\n",a[i]);
    return 0;
}

D. Sum of Maximum Weights

题目大意:

给出一颗 n n n个顶点的树, n − 1 n-1 n1条无向边都有相应的边权,设 f ( u , v ) f(u,v) f(u,v) u u u v v v之间路径上的最长边,求 ∑ i = 1 n − 1 ∑ j = i + 1 n f ( i , j ) \sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n}f(i,j) i=1n1j=i+1nf(i,j)

解题思路:

我们假设连接 u u u v v v的边 e e e是最长的一条边,权重是 w w w,那么这条边就相当于一条独木桥,将 u u u v v v分割成两个连通块,如果要从一个连通块到另一个连通块必须要经过 e e e,设两个连通块的大小分别是 u . s i z e u.size u.size v . s i z e v.size v.size,那么这条边在最终答案中一定贡献了 u . s i z e × v . s i z e u.size\times v.size u.size×v.size次。

通过上述分析,我们发现可以将所有的边按照权重从小到大排序,同时对于每个顶点维护一个并查集,每条边的贡献次数就是这条边所连接的两个连通块大小的乘积,每次加边就相当于将两个连通块合并成一个更大的连通块。

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct Edge{
    int a,b,w;
    bool operator <(const Edge &t)const{
        return w<t.w;
    }
}e[N];
int f[N],sz[N];
int n,m;
int find(int x){
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n-1;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].w);
    sort(e,e+n-1);
    for(int i=1;i<=n;i++) f[i]=i,sz[i]=1;
    long long res=0;
    for(int i=0;i<n-1;i++){
        int a=e[i].a,b=e[i].b,w=e[i].w;
        int fa=find(a),fb=find(b);
        res+=(long long)w*sz[fa]*sz[fb];
        sz[fa]+=sz[fb];
        f[fb]=fa;
    }
    printf("%lld\n",res);
    return 0;
}

E. Packing Under Range Regulations

题目大意:

1 0 9 10^9 109个箱子编号为 [ 1 , 1 0 9 ] [1,10^9] [1,109] n n n个球编号是 [ 1 , n ] [1,n] [1,n],每个箱子最多只能放一个球,其中第 i i i个球要求放在 [ l i , r i ] [l_i,r_i] [li,ri]这个区间的箱子中,问能否存在一个方案满足要求。

解题思路:

做法的核心就是对于第 j j j个箱子,所有 l i ≥ j l_i\ge j lij的球中,我们要优先选择 r i r_i ri最小的球。这个贪心的原因是很直观的。

如果是暴力考虑所有的箱子,肯定是会 T L E TLE TLE的。所以我们考虑只考虑那些有效的区间,通过球来枚举会极大优化时间。是学习官方题解的做法,具体做法看代码。

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
PII a[N];
int n;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&a[i].first,&a[i].second);
        priority_queue<int,vector<int>,greater<int> > heap;
        sort(a+1,a+1+n);
        a[n+1].first=2e9;
        int cur=0;
        bool flag=true;
        for(int i=1;i<=n&&flag;i++){
            if(cur<a[i].first) cur=a[i].first;
            heap.push(a[i].second);
            while(i<n&&a[i+1].first==a[i].first){
                 heap.push(a[i+1].second);
                 i++;
            }
            while(!heap.empty()&&cur<a[i+1].first){
                int t=heap.top();heap.pop();
                if(cur<=t) cur++;
                else{
                    flag=false;
                    break;
                }
            }
        }
        puts(flag?"Yes":"No");
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值