Educational Codeforces Round 120 (Rated for Div. 2)

Posted on 2022-01-25 15:17  Capterlliar  阅读(53)  评论(0编辑  收藏  举报

C. Set or Decrease

题意:给出一组数,可以对每个数进行以下操作:1) 将它-1.     2)将它变成数组中另一个数。给出k,问至少作多少次操作能使数组和小于等于k。

解:首先贪心地想,要么把最小的数变得更小,然后令其他数等于它,要么每个减1。再想想每个减一没有让它变成最小数合算。现在题里有两个变量,一是令最小的数减小x,一是将i个数粘贴为最小值,直到sum≤k。试图二分,但这两个量是关联的,非线性。试图三分,但很麻烦,不是每个值都能解。一般这么麻烦是可以直接算的。算式见代码。最后直接枚举每个i,O(n)求解。顺带一提最后表达式写出来是x+1/x形式,应该是可以三分的qwq

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 200005
#define eps 0.00000001
#define inf 0x7fffffff
//#define int long long
ll n,k;
ll a[maxx],sum[maxx]={0};
signed main() {
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++)
            sum[i]=a[i]+sum[i-1];
        if(sum[n]<=k) {
            printf("0\n");
            continue;
        }
        ll ans=1e18;
        for(int i=0;i<=n-1;i++){
            ll temp=a[1]+ceil(-(k-sum[n-i]+sum[1])/(i+1.0));
            if(temp<0) temp=0;
            ans=min(ans,temp+i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
// suppose that the deletion num is x, and turn i of the n nums to a[1]-x;
// (a[1]-x)*(i+1)+sum[2...n-i]<=k
// x>=a[1]-((k-sum[2...n-i])/(i+1))
// ans=m+x
View Code

D. Shuffle

题意:给出一个01串,每次选一个有k个1的连续区间重新排列组合,问最终有几种结果。

解:排  列  组  合。显然每次选择包含k个1的最长区间先算个组合数,但这样算下来肯定有重复。考虑重复,假设两个区间为 *****         ,那么它们的最大重叠区间会有相同的排列,具体来说,就是从全部重叠个数中选其中1的数量。由于是两个相邻区间,所以重叠部分会有k-1个1。注意模数

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 5005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
ll n,k;
ll C[maxx][maxx]={0};
ll pos[maxx]={0},cnt=0;
char s[maxx];
void init(){
    C[0][0]=1;
    for(int i=1;i<maxx;i++){
        C[i][0]=1;
        for(int j=1;j<maxx;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
}
signed main() {
    init();
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='1')
            pos[++cnt]=i;
    }
    if(k==0||k>cnt){
        printf("1\n");
        return 0;
    }
    ll ans=0;
    ll front=1;
    for(int i=1;i<=cnt-k+1;i++){
        ll p1=pos[i-1]+1;
        ll p2=i+k>cnt?n:pos[i+k]-1;
        ans=(ans+C[p2-p1+1][k])%mod;
        if(i!=1)
            ans=(ans+mod-C[pos[i+k-1]-front-1][k-1])%mod;
        front=pos[i];
    }
    printf("%lld\n",ans);
    return 0;
}
View Code