Codeforces Round #789 (Div. 2) 題解
連結:Codeforces Round #789 (Div. 2)
A. Tokitsukaze and All Zero Sequence
題目大意:
給定長度為\(n\)的陣列,可以進行兩種操作:1、如果\(a_i = a_j\),則使其中一個等於\(0\);2、使\(a_i=a_j=min(a_i,a_j)\)。問用最少的步數使陣列均為\(0\)。
解題思路:
如果陣列中存在\(0\),則使其餘非\(0\)元素與\(0\)進行操作2;如果陣列中存在相同元素,則先對這兩個相同元素進行操作1,再重複第一種情況;對於其餘情況,先進行一次操作2,再視為第二種情況處理。
參考程式碼:
void solve() { vector<int> a(110); int n; cin >> n; for(int i = 0; i < n; i ++) { int x; cin >> x; a[x] ++; } int res = n + 1; for(int i = 0; i <= 100; i ++) { if(i == 0 && a[i] > 0) { res = n - a[i]; break; } if(a[i] >= 2) { res --; break; } } cout << res << '\n'; }
B1. Tokitsukaze and Good 01-String (easy version)
題目大意:
給定一個01字串,每次操作改變一個字元,使得用最少的步數相同字串的長度為偶數。
解題思路:
1、統計當前記錄串的\(0\)或者\(1\)的個數,若個數為奇數,且與下個位置不同,則修改下個位置的字元。
2、將字元下標成對記錄,即{0,1},{2,3}……若出現\(01\)或者\(10\)則表示要修改為\(00\)或者\(11\),需要修改的次數\(+1\)。
參考程式碼:
1、
void solve() { int n; cin >> n; vector<int> a(n); for(int i = 0; i < n; i ++) { char x; cin >> x; a[i] = x - '0'; } int res = 0; int cnt = 1; for(int i = 1; i < n; i ++) { if(a[i] != a[i - 1] ) { if(cnt % 2 == 1) { a[i] = a[i - 1]; res ++; cnt ++; } else { cnt = 1; } } else { cnt ++; } } cout << res << '\n'; }
2、
void solve() { int n; cin >> n; vector<PII> a(n / 2); for(int i = 0; i < n / 2; i ++) { char x, y; cin >> x >> y; a[i].first = x - '0', a[i].second = y - '0'; } int res = 0; for(int i = 0; i < n / 2; i ++) { if(a[i].second != a[i].first) { res ++; } } cout << res << '\n'; }
B2. Tokitsukaze and Good 01-String (hard version)
題目大意:
在B1的基礎上增加了要求最小分成\(0\)串和\(1\)串的個數和。
解題思路:
延續B1第二種的思路,由於\(10\)和\(01\)可以任意的修改成為\(00\)或者\(11\),但是原本的\(00\)或者\(11\)是無法修改的,這樣會增加最小修改次數,因此出現\(xx00xxxx11xx\)的情況一定會被分割成兩部分。同時考慮全為\(10\)或者\(01\)的情況。
參考程式碼:
void solve() {
int n;
cin >> n;
vector<PII> a(n / 2);
for(int i = 0; i < n / 2; i ++) {
char x, y;
cin >> x >> y;
a[i].first = x - '0', a[i].second = y - '0';
}
int res1 = 0, res2 = 0;
int o = -1;
for(int i = 0; i < n / 2; i ++) {
if(a[i].second != a[i].first) {
res1 ++;
} else {
if(o != a[i].first) {
res2 ++;
o = a[i].first;
}
}
}
res2 = max(res2, 1);
cout << res1 << ' ' << res2 << '\n';
}
C. Tokitsukaze and Strange Inequality
題目大意:
給定一種排列\(p\),要求找出四元組的個數。四元組\([a,b,c,d]\)滿足\(p_a<p_c\)平且\(p_b> p_d\)。
解題思路:
由於資料規模,可以暴力選取\(b\)和\(c\),在\([c+1,n]\)中選擇大於\(p_b\)的個數,在\([1,b - 1]\)中選擇小於\(p_c\)的個數,二者相乘。個數可以由\(O(n^2)\)的字首和預處理得到。
參考程式碼:
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++) {
cin >> a[i];
}
vector<vector<int>> cnt1(n + 1, vector<int> (n + 1)), cnt2(n + 1, vector<int> (n + 1));
for(int j = 1; j <= n; j ++) {
for(int i = 1; i <= n; i ++) {
if(a[i] < j) {
cnt1[i][j] ++;
}
}
}
for(int j = 1; j <= n; j ++) {
for(int i = 1; i <= n; i ++) {
cnt1[i][j] += cnt1[i - 1][j];
}
}
for(int j = 1; j <= n; j ++) {
for(int i = n; i >= 1; i --) {
if(a[i] < j) {
cnt2[i][j] ++;
}
}
}
for(int j = 1; j <= n; j ++) {
for(int i = n - 1; i >= 1; i --) {
cnt2[i][j] += cnt2[i + 1][j];
}
}
ll res = 0;
for(int i = 1; i < n; i ++) {
for(int j = i + 1; j < n; j ++) {
int x = cnt1[i - 1][a[j]];
int y = cnt2[j + 1][a[i]];
res += x * y;
}
}
cout << res << '\n';
}
D. Tokitsukaze and Meeting
題目大意:
有\(n\)行\(m\)列的會場,有\(n*m\) 個同學,\(0\)表示頑皮的同學,\(1\)表示認真的同學。同學們按照圖示的順利進入會場,問每一個同學進入會場時有多少行至少有一個認真的同學,有多少列至少有一個認真的同學,答案取二者之和。
解題思路:
發現每個同學的相對列數差是一個定值,我們取會場坐滿時每位同學的列號作為對答案的價值。同時對於行而言,第\(i\)的同學入場時的價值等於第\(i - m\)個同學入場時的價值加上\([i - m + 1, i]\)個同學中有無認真的同學,可以通過記錄上一個同學的編號來簡單判斷。
參考程式碼:
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
vector<int> cres(n * m), rres(n * m);
vector<int> row(n), col(m);
for(int i = 0; i < s.size(); i ++) {
if(s[i] == '1') {
if(col[i % m] == 0) {
col[i % m] = 1;
cres[i] ++;
}
}
}
for(int i = 1; i < n * m; i ++ ) {
cres[i] += cres[i - 1];
}
int l = -INF;
for(int i = 0; i < s.size(); i ++) {
if(i - m >= 0) {
rres[i] = rres[i - m];
}
if(s[i] == '1') {
l = i;
}
if(i - l < m) {
rres[i] ++;
}
}
for(int i = 0; i < n * m; i ++) {
cout << rres[i] + cres[i] << ' ';
}
cout << '\n';
}
E. Tokitsukaze and Two Colorful Tapes
題目大意:
給定兩條長度為\(n\)的綵帶,其中一條顏色排列為\(a\),另一條為\(b\),可以規定每種顏色的分數\(c(1 <= c <= n)\),每個位置對答案的價值為\(|c[a[i]] - c[b[i]]|\),求\(\sum_{i=1}^{n}|c[a[i]] - c[b[i]]|\)的最大值。
解題思路:
首先找出所有環,顯然對於長度為偶數的環,以最大值,最小值,最大值的排列順序所得到的價值最大;對於長度為奇數的環,發現長度為偶數的環中得到最大值的原因是每次差值都是最大值-最小值,但是長度為奇數的的情況下然會有一個值無法發揮作用(例如:最大值-最小值-x-最大值-最小值),這樣x對答案是沒有貢獻的。因此我們對長度為奇數的環每次在中間空一位出來,將多出來的數字填進去即可。
參考程式碼:
void solve() {
int n;
cin >> n;
vector<int> a(n + 1), b(n + 1), f(n + 1);
for(int i = 1; i <= n; i ++) {
cin >> a[i];
}
for(int i = 1; i <= n; i ++) {
cin >> b[i];
f[b[i]] = i;
}
vector<vector<int>> e;
vector<bool> ok(n + 1);
ll res = 0;
for(int i = 1; i <= n; i ++) {
int ne = f[a[i]];
if(!ok[i]) {
vector<int> temp;
temp.push_back(i);
while(a[ne] != a[i]) {
ok[ne] = 1;
temp.push_back(ne);
ne = f[a[ne]];
}
e.push_back(temp);
}
ok[i] = 1;
}
int l = 1, r = n;
vector<int> soc(n + 1);
vector<int> temp;
for(auto x : e) {
if(x.size() % 2 == 0) {
for(int i = 0; i < x.size(); i += 2) {
soc[a[x[i]]] = r --,
soc[a[x[i + 1]]] = l ++;
}
} else {
for(int i = 0; i < x.size(); i ++) {
if(i == x.size() / 2) {
continue;
}
if(i < x.size() / 2) {
if(i % 2 == 0)
soc[a[x[i]]] = r --;
else
soc[a[x[i]]] = l ++;
} else {
if(i % 2 == 1)
soc[a[x[i]]] = r --;
else
soc[a[x[i]]] = l ++;
}
}
temp.push_back(x[x.size() / 2]);
}
}
for(auto x : temp) {
soc[a[x]] = l ++;
}
for(int i = 1; i <= n; i ++) {
// cout << soc[a[i]] << ' ';
res += abs(soc[a[i]] - soc[b[i]]);
}
cout << res << '\n';
}
F. Tokitsukaze and Permutations
題目大意:
有一個排列\(p\),對其進行\(k\)次氣泡排序(從前往後進行)得到新的排列\(a\),記錄陣列\(a\)中每個位置的價值\(v_i = \sum_{j=1}^{i-1}[a_i < a_j]\)。現在已知\(v\)陣列(如果\(v_i = -1\),表示此位置價值未知),問有\(p\)有多少種可能。
解題思路:
發現對於已知的\(v\)僅存在一種排列\(a\)與其對應。進行一次氣泡排序會使\(v_i=max(v_i-1,0)\),然後\(v\)整體左移,\(v_0\)被覆蓋,\(v_{n - 1} = 0\)。做\(v_i\)與\(p_{(i+k)\%n}\)的對映,對\(i<n-k\):\(p_{i+k} < k + i + 1\),\(p_{i+k} <= k + 1 (v_i = 0)\),因此如果\(v_i = -1\),\(i\)位置的貢獻為\(k + i + 1\);如果\(v_i=0\),\(i\)位置的貢獻為\(k + 1\);如果\(v_i>=1 \&\& v_i<i + 1\),\(i\)位置的貢獻為\(1\);其他情況表示不存在這種排列,貢獻為\(0\)。對\(n-k<i<n\):\(p_{(i+k)\%n} < (k + i)\%n + 1\)且\(v_i=0||v_i=-1\),因此\(i\)位置的貢獻為\((k + i) \% n + 1\),不滿足條件則不存在這種排列,貢獻為\(0\)。最後答案取所有貢獻的乘積。
參考程式碼:
void solve() {
int n, k;
cin >> n >> k;
vector<int> v(n);
for(int i = 0; i < n; i ++) {
cin >> v[i];
}
ll res = 1;
for(int i = 0; i < n; i ++) {
if(i < n - k) {
if(v[i] == -1) {
res = (res * (k + i + 1)) % mod;
} else if(v[i] == 0) {
res = (res * (k + 1)) % mod;
} else if(v[i] >= i + 1) {
res = 0;
}
} else {
if(v[i] == 0 || v[i] == - 1) {
res = (res * ((k + i) % n + 1)) % mod;
} else {
res = 0;
}
}
}
cout << res << '\n';
}