Codeforces Round #770(Div.2) [A~D]
連結:contest link
A.Reverse and Concatenate
題目
給定一個長度為 \(n\) 的字串 \(s\) 和一個整數 \(n\) ,用 \(rev(s)\) 表示將字串 \(s\) 反轉,你可以對 \(s\) 執行 \(k\) 次如下操作:
- 將 \(s\) 變為 \(s+rev(s)\)
- 將 \(s\) 變為 \(rev(s)+s\)
求可以得到多少個不同的字串
分析
如果 \(s\) 是迴文串,那麼無論執行哪種操作得到的結果都是一樣的,所以執行 \(k\) 次操作後得到的不同字串數為 \(1\)
如果 \(s\) 不是迴文串,那麼 \(k=0\) 時顯然答案為 \(1\)
程式碼
#include<bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while(t--) { int n, k; cin >> n >> k; string s; cin >> s; if(k == 0) { cout << 1 << endl; continue; } s = ' ' + s; bool flag = true; for(int i = 1; i <= n / 2; i++) { if(s[i] != s[n + 1 - i]) { flag = false; break; } } cout << (flag ? 1 : 2) << endl; } return 0; }
B.Fortune Telling
題目
給定一個長度為 \(n\) 的陣列 \(a\) 和整數 \(x,y\) ,判斷 \(x\) 和 \(x+3\) 進行 \(n\) 次如下操作後能否變為 \(y\) ,第 \(i\) 次操作可以在下面兩條中任選一條執行:
- 將當前數字 \(d\) 替換為 \(d+a_i\)
- 將當前數字 \(d\) 替換為 \(d\oplus a_i\)
保證 \(x\) 和 \(x+3\) 中一定有一個能用適當操作變為 \(y\)
分析
- 若 \(d\) 為偶數
- \(a_i\) 為偶數,無論執行哪種操作 \(d\) 奇偶性都不變
-
\(a_i\) 為奇數,無論執行哪種操作 \(d\)
- 若 \(d\) 為奇數
- \(a_i\) 為偶數,無論執行哪種操作 \(d\) 奇偶性都不變
- \(a_i\) 為奇數,無論執行哪種操作 \(d\) 都會變為偶數
所以每一種操作對於奇偶性的改變是相同的,那麼判斷遍歷陣列執行操作後 \(x,y\) 奇偶性是否相同即可,若不相同則 \(x\) 無論如何都不能變為 \(y\) ,那 \(x+3\) 一定可以
程式碼
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--) {
long long n, x, y, a;
cin >> n >> x >> y;
for(int i = 1; i <= n; i++) {
cin >> a;
if(x % 2 && a % 2) {
x++;
} else if(x % 2 == 0 && a % 2) {
x++;
}
}
cout << (x % 2 == y % 2 ? "Alice" : "Bob") << endl;
}
return 0;
}
C.OKEA
題目
給定 \(n,k\) 構造一個 \(n\times k\) 的矩陣 \(a\) ,滿足:
- \(1\) 到 \(n\cdot k\) 的每個整數都出現在矩陣上且僅出現一次
- 對於任意 \(i,l,r\) 滿足 \(a_{i,l},a_{i,l+1},\cdots,a_{i,r}\) 的算術平均值是整數
分析
可以發現任意一行內的奇偶性是相同的,如果一行內既存在奇數又存在偶數,那麼一定存在一個奇偶相鄰的情況,它們的算術平均值不是整數
若每行的奇偶性都相同,那麼可以構造:
\[\left[\begin{array}{ccc} 1 & 3 & \cdots &2k-1\\ 2 & 4 & \cdots & 2k\\2k+1 & 2k+3 & \cdots & 4k-1\\ \vdots & \vdots & \vdots &\vdots\end{array}\right] \]對任意一段 \(i,l,r\) 進行求和,可以用等差數列求和公式,可以發現和一定是 \(l+r-1\) 的倍數,那麼算術平均值也一定是整數。進一步,可以求出通項公式 \(a_{i,j}=2(j+k\lfloor\frac{i-1}{2}\rfloor)-i\bmod 2\)
程式碼
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--) {
int n, k;
cin >> n >> k;
if((n % 2 && k == 1) || n % 2 == 0) {
cout << "YES" << endl;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= k; j++)
cout << 2 * ((i - 1) / 2 * k + j) - i % 2 << ' ';
cout << endl;
}
} else {
cout << "NO" << endl;
}
}
return 0;
}
D.Finding Zero
題目
題意較複雜,參考原文 Problem D
分析
考慮一個長度為 \(4\) 的序列 \(a,b,c,d\) ,不妨設 \(a\leq b\leq c\leq d\) ,設 \(\overline{a}=\max(b,c,d)-\min(b,c,d)\) ,進行四次詢問可得:
\[\overline{a}=d-b\\ \overline{b}=d-a\\ \overline{c}=d-a\\ \overline{d}=c-a \]易知 \(\overline{b},\overline{c}\) 是四次詢問中結果最大的且 \(c\geq b\geq a\) ,序列中的所有數中只有一個 \(0\) 且其他數都大於 \(0\) ,所以對於任意一個長度為 \(4\) 的序列,詢問結果最大的兩個數一定不是 \(0\) ,那麼我們每次處理長度為 \(4\) 的序列,排除掉不可能為 \(0\) 的兩個不可能為 \(0\) 的選項,再繼續加入兩個數,以此類推
這樣當給定長度 \(n\) 為偶數時,要執行 \(\frac{(n-4)}{2}\times 4+4=2n-4\) 次詢問,\(n\) 為奇數時最後會剩下一個長度為 \(3\) 的序列,我們把之前已經排除的某個選項再次加入,使序列長度變為 \(4\) ,要執行 \(\frac{n-1-4}{2}\times4+2\times 4=2n-2\) 次詢問,滿足要求
程式碼
#include<bits/stdc++.h>
using namespace std;
int n;
int del;
int query(int a, int b, int c)
{
cout << "? " << a << ' ' << b << ' ' << c << endl;
int res;
cin >> res;
return res;
}
vector<int> cal(vector<int> v)
{
vector<pair<int, int> > t;
t.push_back({query(v[1], v[2], v[3]), v[0]});
t.push_back({query(v[0], v[2], v[3]), v[1]});
t.push_back({query(v[0], v[1], v[3]), v[2]});
t.push_back({query(v[0], v[1], v[2]), v[3]});
sort(t.begin(), t.end());
del = t.back().second;
return {t[0].second, t[1].second};
}
void solve()
{
cin >> n;
vector<int>v;
for(int i = 1; i <= n; i++) {
v.push_back(i);
if(v.size() == 4)
v = cal(v);
}
if(v.size() == 2) {
cout <<"! "<< v[0] << ' ' << v[1] << endl;
} else {
v.push_back(del);
v = cal(v);
cout<<"! " << v[0] << ' ' << v[1] << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--) {
solve();
}
return 0;
}