Codeforces Round #734 (Div. 3) 题解
Hello大家好,今天給大家?guī)淼氖?Codeforces Round #734 (Div. 3) 的全題目講解。
本文鏈接:https://www.lanqiao.cn/questions/204012
感謝藍橋云課的 Lqyk 同學提供的題解。
A. Polycarp and Coins
題目鏈接
https://codeforces.com/contest/1551/problem/A
題目大意
有一個人買東西付錢只用一元錢和二元錢, 現(xiàn)在他要付 nnn 元, 他使用一元錢的數(shù)量和二元錢數(shù)量的差值要最小, 問他付 nnn 元使用了多少一元錢和二元錢?
解題思路
差值最小的話一元錢和二元錢的數(shù)量比例要接近 1:11 : 11:1 ,那么總共三元錢, 那么每份都使用的 n/3n / 3n/3 ,最后只要判斷一下剩下的錢是0,1,20, 1, 20,1,2 元的情況即可。
AC_Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){ll n;cin >> n;ll a = n / 3;ll b = n / 3;if(n % 3 == 2) b++;else if(n % 3 == 1) a++;cout << a << " " << b << endl;}return 0; }B1. Wonderful Coloring - 1
題目鏈接
https://codeforces.com/contest/1551/problem/B1
題目大意
-
nnn????? 個字符要么染成紅綠倆種顏色,要么不染色。
-
相同顏色的字符倆倆不同。
-
紅綠倆種顏色的字符數(shù)量相同。
滿足以上三個條件,每種顏色被染色的字符數(shù)量是多少?
解題思路
相同的字符每種顏色最多染一個,因此只要對出現(xiàn)次數(shù) ≥2\geq 2≥2 的字符和出現(xiàn)一次的字符分別討論就行。
- 出現(xiàn)次數(shù) ≥2\geq 2≥2? 的字符, 每種顏色都能分到一個,答案加上這些字符的種類數(shù)。
- 出現(xiàn)一次的,紅一個,綠一個,答案加上這些字符種類數(shù)除以二(向下取整)。
AC_Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){string s;cin >> s;map<char, int> mp;int ans = 0;for(int i = 0;i < s.size();i++) mp[s[i]]++;int cnt = 0;for(auto i : mp){if(i.second >= 2) ans++;else cnt++;}cout << ans + cnt / 2 << endl;}return 0; }B2. Wonderful Coloring - 2
題目鏈接
https://codeforces.com/contest/1551/problem/B2
題目大意
-
nnn 個數(shù)字,染成 kkk? 種顏色,要么不染色。
-
相同顏色的數(shù)字的值是倆倆不同的。
-
所有的 kkk 種顏色的數(shù)字數(shù)量應該相同。
-
染色的數(shù)字的數(shù)量要最多。
用 0?k0 - k0?k 表示染色的顏色種類 (000 表示不染色) ,求滿足上述條件的染色方案。
解題思路
和 B1B1B1 一樣的思路,考慮將 出現(xiàn)次數(shù)≥k\geq k≥k? 的數(shù)字和 出現(xiàn)次數(shù) <k< k<k 的分開討論。
- 出現(xiàn)次數(shù) ≥k\geq k≥k?? 的字符, 每種顏色都能分到一個, 所以把位置存儲下來直接染色即可。
- 出現(xiàn)次數(shù) <k< k<k? 的集中到一起, 為了防止相同的數(shù)字染成同一種顏色,所以先排序在按順序?qū)ο聵巳旧?/li>
AC_Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 50; struct node{int id, val;bool operator < (const node &p) const{return val < p.val;} }; vector<int> num[maxn]; //統(tǒng)計某種數(shù)字出現(xiàn)的次數(shù) vector<node> q; //集中<k的數(shù)字 int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){int n, k;cin >> n >> k;vector<int> ans(n + 1, 0), vis(n + 1, 0), a(n + 1);for(int i = 1;i <= n;i++) num[i].clear(); q.clear();for(int i = 1;i <= n;i++){cin >> a[i];num[a[i]].push_back(i);}for(int i = 1;i <= n;i++){if(num[i].size() >= k){for(int j = 0;j < num[i].size();j++){ans[num[i][j]] = j + 1;}vis[i] = 1;//標記是否>=k}}for(int i = 1;i <= n;i++){if(!vis[a[i]]){q.push_back({i, a[i]});}}sort(q.begin(), q.end());int sum = q.size() / k * k, v = 1;//取整,從1開始染色for(int i = 0;i < sum;i++){ans[q[i].id] = v++;if(v == k + 1) v = 1;}for(int i = 1;i <= n;i++){if(ans[i] > k) cout << 0 << " ";//大于K都是多的,都不染色else cout << ans[i] << " ";}cout << endl;}return 0; }C. Interesting Story
題目鏈接
https://codeforces.com/contest/1551/problem/C
題目大意
從 nnn 個只包含 a、b、c、d、ea、b、c、d、ea、b、c、d、e 的字符串中選擇若干個,使得某一個單一字符的出現(xiàn)次數(shù)大于其余四個總和,問最多可以選多少個字符串。
解題思路
我們可以記錄每個字符串 a、b、c、d、ea、b、c、d、ea、b、c、d、e 出現(xiàn)的次數(shù),然后枚舉 a、b、c、d、ea、b、c、d、ea、b、c、d、e 分別作為大于其他四個字符總和的情況。
假設(shè)當前枚舉 aaa 第 iii 個字符串 aaa 出現(xiàn)的次數(shù)為 sumsumsum, 其余四個總和為 nownownow ,那么他們的差值 sum?nowsum - nowsum?now 為正的時候是可以選的,為了選擇更多,我們貪心的將 sum?nowsum - nowsum?now 的值從大到小排序,不斷加入字符串, 保持總和 ≥0\geq 0≥0即可。
AC代碼
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 50; int a[maxn], b[maxn], c[maxn], d[maxn], e[maxn]; int n; int get_a(){vector<ll> ans;for(int i = 0;i < n;i++){ll sum = a[i];ll now = b[i] + c[i] + d[i] + e[i];ans.push_back(sum - now);}sort(ans.rbegin(), ans.rend());ll k = 0;int cnt = 0;for(int i = 0;i < n;i++){if(k + ans[i] > 0LL) {cnt++;k += ans[i];}else break;}return cnt; } int get_b(){vector<ll> ans;for(int i = 0;i < n;i++){ll sum = b[i];ll now = a[i] + c[i] + d[i] + e[i];ans.push_back(sum - now);}sort(ans.rbegin(), ans.rend());ll k = 0;int cnt = 0;for(int i = 0;i < n;i++){if(k + ans[i] > 0LL) {cnt++;k += ans[i];}else break;}return cnt; } int get_c(){vector<ll> ans;for(int i = 0;i < n;i++){ll sum = c[i];ll now = a[i] + b[i] + d[i] + e[i];ans.push_back(sum - now);}sort(ans.rbegin(), ans.rend());ll k = 0;int cnt = 0;for(int i = 0;i < n;i++){if(k + ans[i] > 0LL) {cnt++;k += ans[i];}else break;}return cnt; } int get_d(){vector<ll> ans;for(int i = 0;i < n;i++){ll sum = d[i];ll now = a[i] + b[i] + c[i] + e[i];ans.push_back(sum - now);}sort(ans.rbegin(), ans.rend());ll k = 0;int cnt = 0;for(int i = 0;i < n;i++){if(k + ans[i] > 0LL) {cnt++;k += ans[i];}else break;}return cnt; } int get_e(){vector<ll> ans;for(int i = 0;i < n;i++){ll sum = e[i];ll now = a[i] + b[i] + c[i] + d[i];ans.push_back(sum - now);}sort(ans.rbegin(), ans.rend());ll k = 0;int cnt = 0;for(int i = 0;i < n;i++){if(k + ans[i] > 0LL) {cnt++;k += ans[i];}else break;}return cnt; } int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){cin >> n;for(int i = 0;i <= n;i++) a[i] = b[i] = c[i] = d[i]= e[i] = 0;for(int i = 0;i < n;i++){string s;cin >> s;for(int j = 0; j < s.size();j++){if(s[j] == 'a') a[i]++;else if(s[j] == 'b') b[i]++;else if(s[j] == 'c') c[i]++;else if(s[j] == 'd') d[i]++;else if(s[j] == 'e') e[i]++;}}int ans = 0;ans = max(ans, get_a());ans = max(ans, get_b());ans = max(ans, get_c());ans = max(ans, get_d());ans = max(ans, get_e());cout << ans << endl;}return 0; }D1. Domino (easy version)
題目鏈接
https://codeforces.com/contest/1551/problem/D1
題目大意
在 $n * m $ 的網(wǎng)格圖放 1×21\times21×2 (橫著)或者 2×12\times12×1(豎著) 的多米若骨牌,在 n?mn * mn?m 為偶數(shù)的情況下,是否能恰好放 kkk 個橫著的多米洛骨牌。
解題思路
考慮為什么題意給的是 n?mn*mn?m 都是偶數(shù),而不是 n、mn、mn、m 都是偶數(shù)。
當 n、mn、mn、m? 都為偶數(shù)的時候, 2×22\times22×2? 的方格可以任意放 $2 $ 個 1×21\times21×2 橫著的或者 2×12 \times 12×1?豎著的,并且它們都是偶數(shù)成對出現(xiàn)的,因此我們就可以分割圖面為若干個 2×22\times22×2的網(wǎng)格, 此時 kkk 為偶數(shù)才能滿足條件。
假設(shè)若 nnn 為奇數(shù), 畫圖不難發(fā)現(xiàn)必定要放 m/2m/2m/2 個橫著的,同理,若 mmm 為奇數(shù), 必定要放n/2n / 2n/2 個豎著的,多出來的行列鋪滿橫的或者豎的后就轉(zhuǎn)化偶數(shù)情況。
因此只要判斷總得減去必定放的是否放得下 kkk?個并且 kkk? 要大于必定放橫的數(shù)量。
AC_Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){int n, m, k;cin >> n >> m >> k;int tot = n * m / 2;if(n % 2){k -= m / 2;tot -= m / 2;}if(m % 2) tot -= n / 2;if(k < 0 || k % 2 || k > tot){cout << "NO\n";continue;}cout << "YES\n";}return 0; }D2. Domino (hard version)
題目鏈接
https://codeforces.com/contest/1551/problem/D2
題目大意
在 n×mn \times mn×m 的網(wǎng)格圖放 1×21\times21×2 (橫著)或者 2×12\times12×1(豎著) 的多米若骨牌,在 n×mn \times mn×m 為偶數(shù)的情況下,是否能恰好放 kkk 個橫著的多米洛骨牌,用字符輸出擺放圖案。
解題思路
D1D1D1 的思路能討論出來后,就按照討論分別構(gòu)造即可。
首先先判斷有沒有奇數(shù)的行、列,從 a?za - za?z? 任意找倆個個字符填充完。
然后將這多的行列暫時刪去,當成 n,mn,mn,m? 都為偶數(shù)填充。
先填充 kkk? 個橫著的,剩下的 2×22\times22×2 填充豎著的即可,唯一的問題就是相鄰的字符會重復的問題,我們可以根據(jù)下標的奇偶關(guān)系來交替填充不同?字符,當然字符從a?za - za?z 自己挑。
AC_Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 200; char ans[maxn][maxn]; int main() {std::ios::sync_with_stdio(false);int t;cin >> t;while(t--){int n, m, k;cin >> n >> m >> k;int tot = n * m / 2;for(int i = 1;i <= n;i++){for(int j = 1;j <= m;j++){ans[i][j] = '#';}}int nn = n, mm = m;if(n % 2){k -= m / 2;tot -= m / 2;int cnt = 1;for(int j = 1;j <= m;j += 2){//最后一行放滿橫的if(cnt % 2) ans[n][j] = ans[n][j + 1] = 'o';else ans[n][j] = ans[n][j + 1] = 'p';cnt++;}n--;}if(m % 2){tot -= n / 2;int cnt = 1;for(int i = 1;i <= n;i += 2){if(cnt % 2) ans[i][m] = ans[i + 1][m] = 'x';else ans[i][m] = ans[i + 1][m] = 'y';cnt++;}m--;}if(k < 0 || k % 2 || k > tot){cout << "NO\n";continue;}int cnt = 1;for(int j = 1;j <= m;j += 2){cnt ++;for(int i = 1;i <= n;i++){if(k != 0){if((i + cnt) & 1) ans[i][j] = ans[i][j + 1] = 'a';else ans[i][j] = ans[i][j + 1] = 'b';k--;}if(!k) break;}if(!k) break;}for(int i = 1;i <= n;i += 2){cnt++;for(int j = 1;j <= m;j++){if(ans[i][j] == '#'){if((j + cnt) & 1) ans[i][j] = ans[i + 1][j] = 'k';else ans[i][j] = ans[i + 1][j] = 'v';}}}cout << "YES\n";for(int i = 1;i <= nn;i++){for(int j = 1;j <= mm;j++){cout << ans[i][j];}cout << endl;}}return 0; }E. Fixed Points
題目鏈接
https://codeforces.com/contest/1551/problem/E
題目大意
給定一個長度為 nnn 的數(shù)組 aaa 和一個常數(shù) kkk。
現(xiàn)有一種操作,操作內(nèi)容為選擇數(shù)組 aaa 中任意一個數(shù) aia_iai? 并刪除它,刪除之后 aia_iai? 右邊的數(shù)將全部向右移動一位,即 ai?1,ai,ai+1,ai+2,...,an→ai?1,ai+1,ai+2,ai+3,...,ana_{i-1},a_i,a_{i+1},a_{i+2},...,a_n\rightarrow a_{i-1},a_{i+1},a_{i+2},a_{i+3},...,a_{n}ai?1?,ai?,ai+1?,ai+2?,...,an?→ai?1?,ai+1?,ai+2?,ai+3?,...,an?。
定義 b1,b2,...,bmb_1,b_2,...,b_mb1?,b2?,...,bm? 為 aaa 進行若干次操作后的數(shù)組,問最少要進行多少次操作,可以使得 bi=ib_i=ibi?=i 的個數(shù)大于等于 kkk。
解題思路
因為本題的數(shù)據(jù)范圍不大 1≤k≤n≤2×1031\leq k\leq n\leq 2\times10^31≤k≤n≤2×103,所以不難想到用 dpdpdp 來解決。
定義 fi,jf_{i,j}fi,j? 表示數(shù)組 aaa 的前 iii 個數(shù),刪除了jjj 個后, bh=h(1≤h≤i)b_h=h(1\leq h\leq i)bh?=h(1≤h≤i) 的最大個數(shù)。
于是當前的這個數(shù) aia_iai?,有兩種決策:
- 刪除 aia_iai?:刪除 aia_iai? 后 aia_iai? 就帶來任何貢獻,即 fi,j=fi?1,j?1f_{i,j} = f_{i-1,j-1}fi,j?=fi?1,j?1?。
- 不刪除 aia_iai?:此時已經(jīng)刪除了 jjj 個數(shù),那么 aia_iai? 的位置為 i?ji-ji?j,所以 fi,j=fi?1,j+[ai==i?j]f_{i,j} = f_{i-1,j} + [a_i == i-j]fi,j?=fi?1,j?+[ai?==i?j]
最后從小到大枚舉刪除的個數(shù) jjj,若 fn,j≥kf_{n,j} \geq kfn,j?≥k,則該 jjj 為答案。
AC_Code
#include<bits/stdc++.h> using namespace std; const int N = 2e3 + 10; int n , k , a[N], f[N][N]; signed main() {int T = 1;cin >> T;while(T --){cin >> n >> k;memset(f , 0 , sizeof(int) * n * n);for(int i = 1 ; i <= n ; i ++) cin >> a[i];for(int i = 1 ; i <= n ; i ++){for(int j = 0 ; j <= i ; j ++){if(!j) f[i][j] = f[i - 1][j] + (a[i] == i - j);else f[i][j] = max(f[i - 1][j - 1], f[i - 1][j] + (a[i] == i - j));}}bool flag = 0;for(int j = 0 ; j <= n ; j ++) if(f[n][j] >= k){cout << j << '\n';flag = 1;break ;}if(!flag) cout << -1 << '\n';}return 0; }F. Equidistant Vertices
題目鏈接
https://codeforces.com/contest/1551/problem/F
題目大意
給定一個包含 nnn 個節(jié)點的樹和一個常數(shù) KKK,要求你樹中選擇 KKK 個節(jié)點并放入集合,使得集合內(nèi)任意兩點之間的距離都相等,問有多少種不同的選擇方法。
解題思路
- 當 K=2K = 2K=2 時,無論怎么選取,集合內(nèi)都只存在一種距離,滿足條件,所以答案為 Cn2C_{n}^{2}Cn2?。
- 當 K>2K > 2K>2 時,則必然存在一個點 uuu,使得集合內(nèi)的點分別位于 uuu 的不同分支上,且到 uuu 的距離相等(證明略),我們定義這樣的 uuu 為集合的中心
我們以 uuu 作為集合的中心,枚舉集合內(nèi)的點到 uuu 的距離,設(shè)當前枚舉的距離為 ddd。
那么在以 uuu 為根的樹里,如果超過 KKK 個分支中存在與 uuu 的距離大于等于 ddd 的節(jié)點,則我們可以選擇從這些分支中挑選 KKK 個作為方案。定義這些分支為有效分支,并有效分支的個數(shù)為 cntcntcnt,那么能帶來的方案數(shù)為 CcntKC_{cnt}^{K}CcntK??不對,因為某些有效分支中可能不僅僅存在一個與 uuu 距離大于等于 ddd 的節(jié)點,它們所能帶來的方案數(shù)不能被忽略,也不好直接計算,于是考慮 dpdpdp!
-
當 vvv 為 uuu 兒子節(jié)點時,f[u][j]=f[v,j?1]f[u][j] = f[v,j-1]f[u][j]=f[v,j?1]
-
當 vvv 為 uuu 父親節(jié)點時:
-
f[u][j]+=f[v][j?1],(j=1)f[u][j] += f[v][j-1] ,(j=1)f[u][j]+=f[v][j?1],(j=1)
-
f[u][j]+=f[v][j?1]?f[u][j?2](j≥2)f[u][j] += f[v][j - 1] - f[u][j - 2] (j\geq2)f[u][j]+=f[v][j?1]?f[u][j?2](j≥2)
-
-
選擇第 jjj 個有效分支:dp[u][j][k]=dp[u][j?1][k?1]×Cf[v][d?1]1=dp[u][j?1][k?1]×f[v][d?1]dp[u][j][k] = dp[u][j - 1][k - 1] \times C_{f[v][d-1]}^{1} = dp[u][j - 1][k - 1] \times f[v][d-1]dp[u][j][k]=dp[u][j?1][k?1]×Cf[v][d?1]1?=dp[u][j?1][k?1]×f[v][d?1].
f[v][d?1]f[v][d-1]f[v][d?1] 表示 vvv 的子樹中與 vvv 距離大于 d?1d-1d?1 的節(jié)點個數(shù),即與 uuu 距離大于等于 ddd 的節(jié)點個數(shù)
Cf[v][d]1C_{f[v][d]}^1Cf[v][d]1? 表示從這些節(jié)點中任意挑選一個 -
不選擇第 jjj 個有效分支:dp[u][j][k]=dp[u][j?1][k]dp[u][j][k] = dp[u][j - 1][k]dp[u][j][k]=dp[u][j?1][k]
對于每次枚舉的距離 ddd ,uuu 對答案的貢獻為 dp[u][cnt][K]dp[u][cnt][K]dp[u][cnt][K]
AC_Code
#include<bits/stdc++.h> using namespace std; const int N = 1e2 + 10, mod = 1e9 + 7; struct Edge {int nex, to; } edge[N << 1]; int head[N] , TOT; int n , K , f[N][N]; long long res , dp[N][N][N]; void add_edge(int u, int v) {edge[++ TOT].nex = head[u];edge[TOT].to = v;head[u] = TOT; } void init() {for(int i = 0 ; i <= n + 1 ; i ++) head[i] = 0;TOT = 0; } void dfs(int u, int far) {for(int i = head[u] ; i ; i = edge[i].nex){int v = edge[i].to;if(v == far) continue ;dfs(v, u);for(int j = 1 ; j <= n ; j ++) f[u][j] += f[v][j - 1];}f[u][0] = 1; } void dfs1(int u , int far) {memset(dp , 0 , sizeof(int) * n * n * n);for(int j = 1 ; j <= n ; j ++){dp[u][0][0] = 1;int cnt = 0;for(int i = head[u] ; i ; i = edge[i].nex){int v = edge[i].to;if(v == far){if(j == 1){dp[u][++ cnt][0] = 1;for(int k = cnt ; k >= 1 ; k --) dp[u][cnt][k] = (dp[u][cnt - 1][k] + dp[u][cnt - 1][k - 1]) % mod;}else{if(f[far][j - 1] - f[u][j - 2] > 0){dp[u][++ cnt][0] = 1;for(int k = cnt ; k >= 1 ; k --) dp[u][cnt][k] = (dp[u][cnt - 1][k] + dp[u][cnt - 1][k - 1] * (f[far][j - 1] - f[u][j - 2]) % mod) % mod;}}}else{if(f[v][j - 1]){dp[u][++ cnt][0] = 1;for(int k = cnt ; k >= 1 ; k --) dp[u][cnt][k] = (dp[u][cnt - 1][k] + dp[u][cnt - 1][k - 1] * f[v][j - 1] % mod) % mod;}}}res += dp[u][cnt][K] , res %= mod;}if(u != 1){for(int j = n ; j >= 1 ; j --){if(j == 1) f[u][j] += f[far][0];else f[u][j] += (f[far][j - 1] - f[u][j - 2]);}}for(int i = head[u] ; i ; i = edge[i].nex){int v = edge[i].to;if(v == far) continue ;dfs1(v, u);} } signed main() {int T = 1;cin >> T;while(T --){init();res = 0;cin >> n >> K ;for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= n + 1; j ++) f[i][j] = 0;for(int i = 1 ; i < n ; i ++){int u, v;cin >> u >> v;add_edge(u, v), add_edge(v, u);}if(K == 2){cout << n * (n - 1) / 2 << '\n';continue ;}dfs(1, 0) , dfs1(1, 0);cout << res << '\n';}return 0; }如有編寫錯誤或者疑惑請評論告訴我,我會第一時間回應的(如果代碼在終測中掛掉了,也請評論告知我謝謝!!!)
本文作者:Lqyk
本文鏈接:https://www.lanqiao.cn/questions/204012
版權(quán)聲明:本作品采用知識共享署名-禁止演繹 2.5 中國大陸許可協(xié)議進行許可。
總結(jié)
以上是生活随笔為你收集整理的Codeforces Round #734 (Div. 3) 题解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新课推荐 | 用 Django 快速搭建
- 下一篇: 实验干货分享:用Go语言实现分布式缓存开