1. 程式人生 > >Codeforces Round #531 (Div. 3) ABCDE題解

Codeforces Round #531 (Div. 3) ABCDE題解

Codeforces Round #531 (Div. 3)

題目總連結:https://codeforces.com/contest/1102

A. Integer Sequence Dividing

題意:

給一個數n,然後要求你把1,2.....n分為兩個集合,使得兩個集合裡面元素的和的差的絕對值最小。

 

題解:

分析可以發現,當n%4==0 或者 n%3==0,答案為0;其餘答案為1。之後輸出一下就好了。

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long
ll; ll n; int main(){ cin>>n; int x = (n+1)/2; if(x%2) cout<<1; else cout<<0; return 0; }
View Code

 

B. Array K-Coloring

題意:

給出n個數,k種顏色(編號為1-k),現在要求給每個數上色,並且每種顏色都要被用到,以及相同的數不能上相同的顏色。

如果最終沒有可行方案就輸出NO,否則將一種上色方案輸出出來。

 

題解:

先判斷可行性,如果存在一種上色方案的話再往後面考慮。

我的做法是先給每個數上色,顏色為該數第幾次出現。然後記錄下出現最多的那個數,對於其它的數,就先從k開始上色。如果k種顏色已經用完,就直接輸出其餘數對應出現的次數。

這種上色方案可以保證同樣的數不會被染為相同的顏色。

還有種更簡單的方案,記錄每個數出現的位置,然後對於每個數的每個位置,不斷染色就好了。當數不同時,染色的起點不會重置。這樣也滿足題中條件。

給出更加簡潔的程式碼吧...

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5005
; int n,k; int a[N],cnt[N],ans[N]; int main(){ cin>>n>>k; vector <int> vec[N]; int ok = 0; if(n<k) ok=1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++){ cnt[a[i]]++; if(cnt[a[i]]>k) ok =1; vec[a[i]].push_back(i); } if(ok) cout<<"NO"; else{ puts("YES"); int col = 1; for(int i=1;i<=5000;i++){ if(vec[i].size()<=0) continue ; for(auto pos : vec[i]){ ans[pos]=col++; if(col>k) col=1; } } for(int i=1;i<=n;i++) cout<<ans[i]<<" "; } return 0; }
View Code

 

C. Doors Breaking and Repairing

題意:

兩個人玩遊戲,一共玩10^100輪。A先手,他可以讓一個門的耐久度減少x;B可以讓一個門的耐久度加上y,但是他不能對耐久度已經為0的門進行操作。

如果一個玩家不能再進行操作,那麼他就直接跳過。

現在想知道,最多可以留下多少扇耐久度大於0的門。

 

題解:

博弈問題。我們對於x,y的大小進行判斷,因為輪數太多,我們就假設會玩無限輪。

當x<=y時,A如果破壞一個門,假設耐久度依然大於0,那麼B會去修復它,這樣A就永遠破壞不了這個門。所以A只能破壞一開始耐久度<=x的門。

A破壞一個門,B可以選擇另外一個耐久度低的門,之後A也是不能破壞這個門的,道理同上。

所以這種情況,假設耐久度小於等於x的門為cnt個,A最多破壞(cnt+1)/2扇門就不行了。

當x>y時,這種情況比較好分析,A總能把所有的門都給破壞。

 

所以程式碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int n,x,y;
int a[N];
int main(){
    scanf("%d%d%d",&n,&x,&y);
    int cnt= 0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]<=x) cnt++;
    }
    if(x>y) cout<<n;
    else{
        cout<<(cnt+1)/2;
    }
    return 0;
}
View Code

 

D. Balanced Ternary String

題意:

給一個長度為3的倍數的字串,裡面只含有0,1,2三種字元。

現在要求你用最少的操作使得三種字元的數量相等,並且字典序要最小。

 

題解:

似乎就是貪心+模擬...

首先可以統計出三種字串的數目,然後如果0的數目比要求的小,那麼說明要將一些1,2換為0。

這裡肯定是儘量從前往後換,因為要求操作次數最小,所以我們先換數量多的就行了。

如果0的數量比要求的多,那麼就儘量從後往前換0,因為是從後往前,如果cnt[2]>=cnt[1],我們就優先將0換為2;否則就換為1。

注意不能一直換下去,中途判斷一下是否0,1,2的數量達到了要求。

當0的數量達到要求後,再來換1或者2,這種情況就比較簡單了,分析類似上面。

 

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+5;
int n;
char s[N];
int cnt[N],a[N];
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    for(int i=0;i<n;i++) a[i+1]=s[i]-'0',cnt[a[i+1]]++;
    int lev = n/3;
    if(cnt[0]>lev){
        int fir;
        if(cnt[1]>=cnt[2]) fir=2;
        else fir=1;
        for(int i=n;i>=1;i--){
            if(cnt[0]==lev) break ;
            if(!a[i]){
                cnt[fir]++;
                cnt[0]--;
                a[i]=fir;
                if(cnt[fir]>=lev) fir = 3-fir;
            }
        }
    }else if(cnt[0]<lev){
        int fir;
        if(cnt[1]>cnt[2]) fir=1;
        else fir = 2;
        for(int i=1;i<=n;i++){
            if(cnt[0]==lev) break ;
            if(a[i]==fir){
                cnt[fir]--;
                cnt[0]++;
                a[i]=0;
                if(cnt[fir]<=lev) fir=3-fir,i=0;
            }
        }
    }
    if(cnt[1]<lev){
        for(int i=1;i<=n;i++){
            if(cnt[1]>=lev) break;
            if(a[i]==2){
                cnt[2]--;
                cnt[1]++;
                a[i]=1;
            }
        }
    }else if(cnt[1]>lev){
        for(int i=n;i>=1;i--){
            if(cnt[1]<=lev) break ;
            if(a[i]==1){
                cnt[1]--;
                cnt[2]++;
                a[i]=2;
            }
        }
    }
    for(int i=1;i<=n;i++) cout<<a[i];
    return 0;
}
View Code

 

E. Monotonic Renumeration

題意:

給出一個a陣列,現在要求構造一個b陣列滿足如下條件:

1.b1=0 ; 

2.對於所有的1<=i,j<=n且i!=j,如果ai=aj,那麼bi=bj;

3.對於所有的1<=i<n,有bi+1=bi+1或者bi+1=bi

 

題解:

如果al=ar,那麼我們知道區間[l,r]中,b陣列的值都是一樣的。

根據這點找區間覆蓋即可:將重合的區間看作一個區間,然後對於每個區間,除開第一個區間對答案的貢獻是1,其餘區間對答案的貢獻都為2。

尋找重合區間可以利用一個指標,o(n)就可以完成。

 

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5,MOD = 998244353 ;
map <int,int>r;
int n;
int a[N];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        r[a[i]]=i;
    }
    ll ans = 1;
    for(int i=1;i<=n;i++){
        int cur = r[a[i]];
        if(i>1){
            ans*=2;
            if(ans>=MOD) ans-=MOD;
        }
        while(i<cur){
            i++;
            cur=max(r[a[i]],cur);
        }
    }
    cout<<ans;
    return 0;
}
View Code