1. 程式人生 > 其它 >Codeforces Global Round 18 A-C題解

Codeforces Global Round 18 A-C題解

csdn轉過來的,我是yzh本人,不是抄的文章

A. Closing The Gap

題意:

給$n$個整數$a_1$...$a_n$,有一種操作:

選擇兩個a,b使a+=1,b-=1

問經過若干次操作後,$Max(a_i...a_n)-Min(a_i...a_n)$的最小值

思路:

想水流一樣,這個高度差的最小值不可能>1(可自行證明)
如果$\sum_{i=1}^n a_i$ $mod$ $n$為0那麼直接輸出0
若不為0則輸出1

程式碼:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
#define int long long
int t,n,ans;
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        ans=0;
        for(int i=1;i<=n;++i){
            int a;
            cin>>a;
            ans+=a;
        }
        if(ans%n==0) puts("0");
        else puts("1");
    }
    return 0;
}

B. And It's Non-Zero

題意:

給定$l$和$r$,數列為$l...r$,問至少刪除多少個元素可以使按順序進行與(&)運算的結果不為0

思路:

居然只要不為0就可以那我們只要確定$l...r$的數轉換成二進位制中,1最多的那一位,然後用數列中數的個數減去這一位的個數。例如:

l=1,r=4

那麼轉換成二進位制就是:

4:1 0 0
3:0 1 1
2:0 1 0
1:0 0 1

定義記錄一的陣列為sum,則:

sum:1 2 2

輸出結果為4-2=2
所以只管留最多1的位就是最優解。

程式碼:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
int T,l,r;
int sum[200010][25];
int main(){
    // scanf("%d",&T);
    cin>>T;
    for(int i=1;i<=200000;++i){
        for(int j=1;j<=25;++j){
            sum[i][j]=sum[i-1][j];
        }
        int it=i;
        int jt=0;
        while(it){
            ++jt;
            sum[i][jt]+=(it%2);
            it/=2;
        }
    }
    while(T--){
        // scanf("%d%d",&l,&r);
        cin>>l>>r;
        int maxn=-1;
        for(int i=1;i<=25;++i){
            maxn=max(maxn,sum[r][i]-sum[l-1][i]);
        }
        // printf("%d\n",(r-l+1)-maxn);
        cout<<(r-l+1)-maxn<<endl;
    }
    return 0;
}

注意預處理的過程不要卡陣列的線,要不然會導致一直亂輸出
(昨天就因為這個調了半天)

C. Menorah

題意:

給兩個長度為$n$的字串a和b,給定一種操作:

固定a中一個‘1’,將其他字元反轉(0變1,1變0)

問至少進行多少次操作可以使字串a變成b。

思路:

我們不難發現,每進行兩次操作我們其實都是交換了一對1和0

證明:
若第一次操作選擇了第i位的1,那麼第二次再選擇第i位就是無意義的操作
第二次操作選擇第j位的1,由於在第一次操作前第j位為0,也就是說,將操作後的第j位保留了下來,而其他反轉
反轉過後除了第i位都回歸第一次操作前,而第i位變成了0
也就是說將一對1和0進行了交換

然後我們定義三個陣列:

sumb為陣列b中‘1’的個數,suma同理,dif為a和b有幾位不同

若$sumb=suma$我們可以直接從a交換到b,輸出答案為dif
然後我們發現倒數第二個樣例過不了(不得不說這次出的樣例是真的良心),通過樣例解釋我們發現,可以將字串a轉換到除了第i位與b第i位都為1外其他任意一位都不相等(詳情見cf),那麼我們只需要固定第i位做一次操作即可,所以我們得出來下面一種可行的方案:

若n-suma==sumb-1(若成立則可以通過若干次操作後得到”字串a轉換到除了第i位與b第i位都為1外其他任意一位都不相等“這樣一個串,然後固定一個1反轉即可)
這種情況下答案就是(n-1)-dif+1
解釋:第一個(n-1)-dif是指保留一位與b串相等的情況下把其他位都變為不等的操作次數,最後+1是最後的一次操作

程式碼:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
int T,n,suma,sumb,dif,ans;
string a,b;
int main(){
    // scanf("%d",&T);
    cin>>T;
    while(T--){
        // scanf("%d",&n);
        cin>>n;
        cin>>a>>b;
        ans=inf;
        suma = sumb = dif = 0;
        for(int i=0;i<n;++i){
            if(b[i]=='1') sumb++;
            if(a[i]=='1') suma++;
            if(a[i]!=b[i]) dif++;
        }
        if(suma==sumb){
            ans=min(ans,dif);
        }
        if(n-suma==sumb-1){
            ans=min(ans,n-dif);
        }
        if(ans==inf){
            puts("-1");
        }
        else printf("%d\n",ans);
    }
    return 0;
}