Codeforces Round #782 (Div. 2)
A. Red Versus Blue
題意
紅隊和藍隊正在比賽,給出比賽場次 \(n\),紅隊贏的場次 \(a\), 藍隊贏的場次 \(b\)
求出一個比賽情況,使得每個隊伍連續獲勝的場次最少。
思路
-
\(a >= b\), 那麼問題可以轉化為 在 \(b + 1\) 個 空隙 中,填 \(a\),使得連續的 \(a\) 最少
我們河狸分配 \(a / (b + 1)\) 以及 \(a % (b + 1)\) 即可 -
\(a < b\) 我們直接交換 \(a, b\) 即可
$\color{red}{SOLUTION} $
點選檢視程式碼
void solve() { int n, a, b; cin >> n >> a >> b; char c1 = 'R', c2 = 'B'; if(a < b) { swap(a, b); swap(c1, c2); } int x = a / (b + 1), y = a % (b + 1); string ans; string res(x, c1); //處理最左邊那個空隙 ans = res; if(y) { ans += c1; y --; } for(int i = 0; i < b; i ++ ) { ans += c2; ans += res; if(y) { ans += c1; y --; } } cout << ans << "\n"; }
B. Bit Flipping
題意
給定一個長度為 \(n\) 且只包含 \(0\) 和 \(1\) 的字串,對這個字串進行 \(k\) 次操作、
每次操作可以選擇一個字元,並且把除了這個字元之外的字元都 \(0\) 變 \(1\), \(1\) 變 \(0\)
求出恰好 \(k\) 次操作後的字串轉成十進位制的最大值
思路
我們對 \(k\) 分 奇、偶 討論
當 \(k\) 為奇數的時候,從高位到低位進行貪心,假設當前位為 \(1\),那麼我們就要使得 \(k\) 次操作中有奇數次是選擇的當前位,這樣可以保證當前為最後是 \(1\),那麼我們可以讓這一位操作 \(1\) 次 (這樣可以有更多的操作次數去讓後面的位都置一)
這樣操作完,我們可以儘可能的保證高位為 \(1\)
加入操作次數仍有剩餘,我們可以將所有的次數都給最後一位即可,這樣可以保證答案是最優解。
\(k\) 為偶數同理。
$\color{red}{SOLUTION} $
點選檢視程式碼
void solve() { int n, k; cin >> n >> k; string s; cin >> s; vector<int> a(n); if(!(k & 1)) { for(int i = 0; i < n && k; i ++ ) if(s[i] == '0') { a[i] = 1; k --; } a[n - 1] += k; for(int i = 0; i < n; i ++ ) if(a[i] & 1) { s[i] = s[i] ^ '1' ^ '0'; } cout << s << "\n"; for(int i = 0; i < n; i ++ ) { cout << a[i] << " \n"[i == n - 1]; } } else { for(int i = 0; i < n && k; i ++ ) if(s[i] == '1') { a[i] = 1; k --; } a[n - 1] += k; for(int i = 0; i < n; i ++ ) if(!(a[i] & 1)) { s[i] = s[i] ^ '1' ^ '0'; } cout << s << "\n"; for(int i = 0; i < n; i ++ ) { cout << a[i] << " \n"[i == n - 1]; } } }
C. Line Empire
題意
有一個國王處於一維座標軸的原點處,他想征服處於正半軸的其他國家,每次攻打只可以攻打距離他最近的沒有攻打過的國家,國王也可以選擇移動到附近的國家。
假設國王處於 \(c_1\),攻打的國家處於 \(c_2\),那麼征服的代價是 \(a \times |c_1 - c_2|\)
假設國王處於 \(c_1\),移動的國家處於 \(c_2\),那麼移動的代價是 \(b \times |c_1 - c_2|\)
注:國王只可以移動到已經被征服的國家 , 國王只可以征服距離他最近的沒有被征服的國家,即不可以隔山打牛
思路
經過觀察我們可以發現,我們移動的最終點只有一個(好像說了一句廢話)
我們可以列舉我們要移動的終點,我們計算出從起點到這個終點的代價,以及從這個移動的終點一直到最後一個國家的代價,找到最小值即是答案。
$\color{red}{SOLUTION} $
點選檢視程式碼
void solve() {
int n, a, b; cin >> n >> a >> b;
vector<int> x(n + 1);
for(int i = 1; i <= n; i ++ ) cin >> x[i];
vector<ll> f1(n + 1), f2(n + 1), f(n + 1);
//起點到這個終點的代價
for(int i = n - 1; i >= 0; i -- ) {
f1[i] = f1[i + 1] + (ll)(x[i + 1] - x[i]) * (n - i);
}
//這個移動的終點一直到最後一個國家的代價
for(int i = 1; i <= n; i ++ ) {
f2[i] = f2[i - 1] + (ll)(x[i] - x[i - 1]);
f[i] = (ll)a * (x[i] - x[0]);
}
ll ans = INT64_MAX;
//我計算的是 b 的個數,因此要乘以一個 b
for(int i = 0; i <= n; i ++ ) {
ans = min(ans, f1[i] * b + f2[i] * b + f[i]);
}
cout << ans << "\n";
}
D. Reverse Sort Sum
題意
定義 \(f(k,A)\) 表示對陣列 \(A\) 的前 \(k\) 個數字排序得到的新陣列
\(A\) 是一個包含 \(0\) 和 \(1\) 的陣列
陣列 \(C\) 是 $f(1, A) + f(2,A) + ... f(n,A) $ 的對應位置的元素和
給定陣列 \(C\),求出原始陣列 \(A\)
思路
經過倒序來判斷最後一位是否是 \(1\),即可求出最後的答案,我們需要的操作就是區間修改以及單點求值
那麼我們可以維護一個差分樹狀陣列來實現這一操作
我們在第 \(i\) 位 減去的貢獻就是 把 \([i-cnt+1, i]\) 這個區間的數字都減去 \(1\), \(cnt\) 是當前還剩的 \(1\) 的個數
原因是 \(f(k,A)\) 是有序的,因此他的所有 \(1\) 都在最後面。
$\color{red}{SOLUTION} $
點選檢視程式碼
struct Bit {
int n;
vector<int> tr;
Bit() {};
Bit(int _n) { n = _n; tr.resize(n + 1); };
void add(int a, int b) {
while(a <= n) tr[a] += b, a += a & -a;
}
int sum(int a, int ret = 0) {
while(a > 0) ret += tr[a], a -= a & -a;
return ret;
}
};
void solve() {
int n; cin >> n;
vector<int> a(n), ans(n);
Bit f(n);
for(int i = 0; i < n; i ++ ) {
cin >> a[i];
if(!i)
f.add(i + 1, a[i]);
else
f.add(i + 1, a[i] - a[i - 1]);
}
int cnt = accumulate(a.begin(), a.end(), 0ll) / n;
for(int i = n - 1; i >= 0; i -- ) {
int now = i - cnt + 2;
if(f.sum(i + 1) == i + 1) {
ans[i] = 1;
cnt --;
}
f.add(now, -1);
}
for(int i = 0; i < n; i ++ ) {
cout << ans[i] << " \n"[i == n - 1];
}
}