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 longView Codell; ll n; int main(){ cin>>n; int x = (n+1)/2; if(x%2) cout<<1; else cout<<0; return 0; }
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 = 5005View Code; 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; }
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