Codeforces Round #643 (Div. 2) / contest 1355


A B C D E F

( √:做出; ●:尝试未做出; ○:已补题 )


题目地址:https://codeforces.com/contest/1355



A Sequence with Digits

题意 a n + 1 = a n + m i n D ( a n ) m a x D ( a n ) a_{n+1}=a_n+minD(a_n)*maxD(a_n) ,其中 minD 表示数位中最小的数,maxD表示数位中最大的数,给定 a 1 a_1 K K ,求 a K a_K 。( 1 a 1 1 0 18 , 1 K 1 0 16 1\leq a_1\leq 10^{18},1\leq K\leq 10^{16}

思路:数据范围一开始吓了我一跳,不过转念一想,如果 minD 等于0,那么以后的minD都会等于0,所以就一直计算到minD等于0就可以了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
LL minD(LL x)
{
    LL ans=x%10;
    x/=10;
    while(x)
    {
        ans=min(ans,x%10);
        x/=10;
    }
    return ans;
}
 
LL maxD(LL x)
{
    LL ans=x%10;
    x/=10;
    while(x)
    {
        ans=max(ans,x%10);
        x/=10;
    }
    return ans;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    LL a,k;
    while(T--)
    {
        scanf("%lld%lld",&a,&k);
        k--;
        while(k--)
        {
            LL x=minD(a),y=maxD(a);
            if(!x) break;
            a+=x*y;
        }
        printf("%lld\n",a);
    }
 
    return 0;
}



B Young Explorers

题意:n 个人,每个人有一个属性值 a i a_i ,现在要给所有人组队(不要求每个人都要入队),要求任意一个人所在的队伍的总人数必须不小于其属性值。问最多能组多少队?

思路:贪心。从属性值小到大枚举,每当选的人数大于等于当前这个人的属性值时新增一队。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
const int maxn=2e5+5;
int n,T,a[maxn];
 
int main()
{
    //freopen("input.txt","r",stdin);
    T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        sort(a+1,a+n+1);
        int ans=0,tot=0,maxx=-1e8;
        REP(i,1,n)
        {
            maxx=max(a[i],maxx);
            tot++;
            if(tot>=maxx) ans++,tot=0,maxx=-1e8;
        }
        printf("%d\n",ans);
    }
 
    return 0;
}



C Count Triangles

题意:给定四个正整数 A, B, C, D (5e5),然后 x, y, z 的取值要满足 A x B y C z D A\leq x\leq B\leq y\leq C\leq z\leq D ,问有多少种取值,可以构成严格的三角形(不能共线)。

思路:这道题卡了好久……思路很明确,z的长度可以遍历,然后对于每一个z,要在O(1)的时间内计算出取值个数,也就是 x + y > z x+y>z 的取值种数。这要分两类,一类是当 y = B y=B 的时候 x 可计算,另一类是当 x = B x=B 的时候 y 可计算,然后每一类中分三个小类,也就是当某个等于 B的时候另一个的最小值在另一个所属区间的哪一部分,然后分类计算即可。

官方题解给出了另一种方法,真是太厉害了:只用计算 x + y = z x+y=z 的选取种数,然后 x + y > z x+y>z 就可以用前缀和。至于计算 x + y = z x+y=z 的时候,可以开一个大数组 a[i] 表示 x+y=i 的种数,然后枚举 x 的取值,用差分的思想把 a[i+B,…,i+C] 上所有数 +1,最后两次前缀和,然后枚举 z 计算即可。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
int A,B,C,D;
 
LL cal(int z)
{
    if(B+C<=z) return 0;
    if(z-B+1<=B)
    {
        if(z-B+1<=A) return 1ll*(B-A+1)*(C-B+1);
        int temp=z-B+1-A+1;
        if(temp>=C-B+1)
        {
            int a1=B-(z-B+1)+1,n=C-B+1;
            return 1ll*a1*n+1ll*n*(n-1)/2;
        }
        else
        {
            int a1=B-(z-B+1)+1,n=temp;
            return 1ll*a1*n+1ll*n*(n-1)/2+1ll*(C-B+1-temp)*(B-A+1);
        }
    }
    else
    {
        int l=z-B+1;
        if(l>C) return 0;
        if(C-l+1<=B-A+1) return 1ll*(C-l+1)*(C-l+2)/2;
        else
        {
            int a1=C-l+1,n=B-A+1;
            return 1ll*a1*n-1ll*n*(n-1)/2;
        }
    }
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    cin>>A>>B>>C>>D;
    LL ans=0;
    REP(z,C,D) ans+=cal(z);
    cout<<ans;
 
    return 0;
}



D Game With Array

题意:给定 N 和 S,问是否存在一个长度为 N 和为 S 的正整数数列,使得对于某个给定的正整数 K,不能在这个数列中选取某个子集,使得该子集的和等于 K 或 S-K。如果可以给出这个数列的每一项以及 K 的值。

思路:其实这道题我做题的时候是猜的。因为我发现当 S 2 N S\geq 2N 的时候,只要构造数列 { 1 , 1 , 1 , . . . , S N + 1 } \{1,1,1,...,S-N+1\} ,然后令 K = S / 2 K=S/2 ,就可以满足要求;对于 S < 2 N S<2N 的情况,我试了几个例子,发现都不行,所以就假装这是对的了(实际确实是对的)。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    int n=read(),s=read();
    if(n*2>s) puts("NO");
    else
    {
        puts("YES");
        REP(i,1,n-1) printf("1 ");
        printf("%d\n",s-n+1);
        printf("%d\n",s/2);
    }
 
    return 0;
}



E Restorer Distance

题意:初始有 n 根柱子,每根柱子有一个高度 h[i],现在有三种操作:花费 A 使得某根柱子高度+1、花费 R 使得某根柱子高度-1、花费 M 使得某根柱子高度-1而另一根+1。问最少的花费,使得最终所有柱子高度一样。

思路:一般这种正面思考毫无头绪的,那就要考虑一下dp或者模型转换什么的。因为是高度一样所以我考虑过差分,但是这三种操作在差分数组中反而更加复杂了,所以应该不行。后来想到二分,进而想到如果对于某个确定的一致高度 h,是可以算出最少花费的:首先如果 M A + R M\geq A+R ,那么就不用考虑操作三,因为操作三可以转换为操作一加操作二,那就直接计算就好了;如果 M < A + R M<A+R ,那么就把所有柱子根据大于小于 h 分类,然后尽可能使用操作三,最后剩下的再用操作一或操作二,也可以很快计算出最小花费。然后我根据样例枚举了所有可能的 h 并计算最小花费,发现花费满足二次函数性质,所以三分的思路就很明显了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
const int maxn=1e5+5;
int h[maxn],n,a,r,m;
 
LL cost(int height)
{
    int lown=0,highn=0;
    LL lows=0,highs=0;
    REP(i,1,n)
    {
        if(h[i]<height) lows+=h[i],lown++;
        else if(h[i]>height) highs+=h[i],highn++;
    }
    LL x=1ll*lown*height-lows;
    LL y=highs-1ll*highn*height;
    if(m<a+r)
    {
        if(x<y) return 1ll*x*m+1ll*(y-x)*r;
        else return 1ll*y*m+1ll*(x-y)*a;
    }
    else return x*a+y*r;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    n=read(),a=read(),r=read(),m=read();
    REP(i,1,n) h[i]=read();
    //REP(i,1,10) cout<<i<<' '<<cost(i)<<endl;
    int l=0,r=1e9;
    while(l<r-5)
    {
        int ml=l+(r-l)/3,mr=l+(r-l)*2/3;
        if(cost(ml)<cost(mr)) r=mr;
        else l=ml;
    }
    LL ans=1e18;
    REP(i,l,r) ans=min(ans,cost(i));
    cout<<ans;
 
    return 0;
}



F

题意

思路

代码


猜你喜欢

转载自blog.csdn.net/dragonylee/article/details/106171227