1. 程式人生 > 其它 >Educational Codeforces Round 120 (Rated for Div. 2)

Educational Codeforces Round 120 (Rated for Div. 2)

嗚,數學,庫魯西

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