Codeforces Round #668 (Div. 1)
A. Balanced Bitstring
題意:
給一個01串,其中某些位置為'?',即可以為0也可以為1。
並且會給出一個 \(k\),\(k\) 是偶數,現在問是否可以使得所有長度為 \(k\) 的子串中都包含相等的0和1。
思路:
顯然可以發現該串存在一個長度為 \(k\) 的迴圈節。
也就是說每隔 \(k\) 個位置的數一定相等,根據這一點來做就好了。
Code
// Author : heyuhhh // Created Time : 2020/09/16 11:04:53 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n, k; cin >> n >> k; string s; cin >> s; for (int i = 0; i < k; i++) { bool has_0 = false, has_1 = false; for (int j = i; j < n; j += k) { if (s[j] == '0') has_0 = true; if (s[j] == '1') has_1 = true; } if (has_0 && has_1) { cout << "NO" << '\n'; return; } if (has_0) { for (int j = i; j < n; j += k) { s[j] = '0'; } } if (has_1) { for (int j = i; j < n; j += k) { s[j] = '1'; } } } vector<int> cnt(2); for (int i = 0; i < k; i++) { if (s[i] == '0') ++cnt[0]; else if (s[i] == '1') ++cnt[1]; } if (cnt[0] <= k / 2 && cnt[1] <= k / 2) { cout << "YES" << '\n'; } else { cout << "NO" << '\n'; } } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
B. Tree Tag
題意:
給定一顆 \(n\) 個點的無根樹,現在有兩個人分別位於結點 \(a\) 和 \(b\)。現在第一個人每次最多能跳 \(da\) 距離,第二個人最多能跳 \(db\) 距離。
如果存在某一時刻兩個人位於同一個點,那麼第一個人就win;否則第二個人win。
思路:
題意就是第一個人要抓第二個人,第二個人就負責跑。
我們可以分情況來做做:
- 如果 \(A\) 上來就能抓住顯然就沒了;
- \(2\cdot da\geq L\),\(L\) 為樹的直徑。這顯然 \(A\) 只需要跳到樹的重心,之後便可跳到任意一個位置;
- 現在一定存在死角,假設 \(B\) 位於死角,\(A\)
- 若 \(db\leq 2 * da\),那 \(B\) 鐵定跑不掉;
- 否則 \(B\) 還是有機會跑掉的,這個較為顯然,注意現在的情況存在一個前提:\(L>da\ \&\&\ db>2*da\)。
就分上面幾種情況來做,還是有點細節。
Code
// Author : heyuhhh // Created Time : 2020/09/07 09:29:10 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n, a, b, da, db; cin >> n >> a >> b >> da >> db; --a, --b; vector<vector<int>> G(n); for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; --u, --v; G[u].push_back(v); G[v].push_back(u); } vector<int> dep(n), up(n); int L = 0, rt = 0; function<void(int, int)> dfs = [&] (int u, int fa) { up[u] = fa; if (dep[u] > L) { L = dep[u]; rt = u; } for (auto v : G[u]) if (v != fa) { dep[v] = dep[u] + 1; dfs(v, u); } }; dfs(0, -1); dep[rt] = 0; int node = rt; dfs(rt, -1); int x = a, y = b; if (dep[a] < dep[b]) swap(a, b); while (dep[a] > dep[b]) a = up[a]; while (a != b) a = up[a], b = up[b]; int dis = dep[x] + dep[y] - 2 * dep[a]; if (da >= dis || min(L, db) <= 2 * da) { cout << "Alice" << '\n'; } else { cout << "Bob" << '\n'; } } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
C. Fixed Point Removal
題意:
給定一個序列 \(a\),現在可以執行若干次下面的操作:
- 若 \(a_i=i\),那麼可以刪除 \(a_i\),後面的元素往前移動。
然後回答若干組詢問,每組詢問為一段區間,表示只能刪除區間 \([l,r]\) 中的數。
對於每組詢問回答最多能夠執行多少次上述操作。
思路:
一個較為顯然的想法是令 \(a_i=i-a_i\),那麼每次能夠刪除一個為 \(0\) 的位置,後面的元素都減去 \(1\)。
如果沒有詢問的話那麼就是一個貪心,我們每次刪除最後面的那個 \(0\) 就行。
但現在有詢問就比較難搞,仔細思考其實可以注意到如果固定左端點,我們不斷增加右端點的話就很好做,具體來說假設當前 \([l,r]\) 的答案為 \(x\),之後右端點要增加 \(1\)。那麼如果 \(0\leq a_{r+1}\leq x\),\([l,r+1]\) 的答案將變為 \(x+1\)。我們不用管是如何操作的,但是最後最優方法一定可以使得答案增加 \(1\)。
但是現在有很多個區間,左端點還會移動,這樣不好判斷。
我們還是考慮從左往右移動右端點,用一個數組比如 \(f[i]\) 表示左端點為 \(i\) 時的答案。容易發現 \(f[1]\geq f[2]\geq\cdots\geq f[r]\)。那麼根據這一點,我們右端點移動時,會有一個字首增加 \(1\),現在就很容易通過資料結構去維護了。
現在只需將詢問離線,在訪問到詢問區間右端點時統計答案即可。
細節見程式碼:
Code
// Author : heyuhhh
// Created Time : 2020/09/16 15:46:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = (1 << 19);
struct FT {
int c[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (++x; x < N; x += lowbit(x)) {
c[x] += v;
}
}
int query(int x) {
int res = 0;
for (++x; x; x -= lowbit(x)) {
res += c[x];
}
return res;
}
int find(int x) {
int p = 0;
for (int i = 18; i >= 0; i--) {
if (query(p | (1 << i)) >= x) {
p |= (1 << i);
}
}
return p - 1;
}
} bit;
void run() {
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
--a[i];
a[i] = i - a[i];
}
vector<vector<pii>> Q(n);
for (int i = 1; i <= q; i++) {
int x, y;
cin >> x >> y;
int l = x, r = n - y - 1;
Q[r].emplace_back(l, i);
}
vector<int> ans(q + 1);
for (int r = 0; r < n; r++) {
if (a[r] >= 0) {
int L = 0, R = r + 1, mid;
while (L < R) {
mid = (L + R) >> 1;
if (bit.query(mid) >= a[r]) {
L = mid + 1;
} else {
R = mid;
}
}
// dbg(r, L);
bit.add(0, 1), bit.add(L, -1);
}
for (auto& it : Q[r]) {
int l = it.fi, id = it.se;
ans[id] = bit.query(l);
}
}
for (int i = 1; i <= q; i++) {
cout << ans[i] << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
D. Game of Pairs
題意:
首先會給定一個 \(n\),然後可以選擇先手或者後手:
- 如果選擇先手,將 \(1,2,\cdots,2n\) 兩兩分組,後手此時只能從每組裡面選擇一個,如果選出來的數的和是 \(2n\) 的倍數就輸了,否則就贏;
- 如果選擇後手,那麼會給出來一個類似於上面的分組,現在你要選出一些數,如果這些數的和為 \(2n\) 的倍數那麼就贏了,否則就輸。
現在就問怎麼操作能夠必勝。
思路:
如果 \(n\) 為偶數的話,那麼類似於 \((1,n+1),(2,n+2),\cdots,(n,2n)\) 這樣分組能夠必勝。
原因在於所有組中的兩個數在模 \(n\) 意義下就是相等的。那麼後者必然選出來的數滿足 \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}(mod\ n)\)。
分式很難看,令 \(n=2m\),就有 \(m\cdot(2m-1)(mod\ 2m)\)。因為 \(2m-1\) 為奇數,那麼式子顯然不為 \(0\),也就是說不整除 \(n\),顯然也不整除 \(2n\)。
如果 \(n\) 為奇數的話,需要下面的觀察:
- 所有數的和為 \(n\cdot(2n+1)\),其在模 \(2n\) 意義下為\(n\)。那麼如果我選出來的數在模 \(n\) 意義下為 \(0\),就能找到答案。
因為此時如果 \(\% 2n\) 為 \(0\),就是答案;如果為 \(n\),我全部選另外的數,那就是答案。
還有另外一個觀察:
- \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}\),此時 \(n\) 為奇數,那麼一定是 \(n\) 的倍數。
所以現在我們就選出一些在模 \(n\) 意義下兩兩不同的數即可。
這個是能夠選出來的,我們連邊 \(i\rightarrow
i+n\),以及分組中的數互相連邊,那麼最終一定會形成若干個長度為偶數的環。之後對每個環奇偶染色即可。
Code
// Author : heyuhhh
// Created Time : 2020/09/16 18:33:32
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n;
cin >> n;
if (n % 2 == 0) {
cout << "First" << '\n';
for (int i = 0; i < 2 * n; i++) {
if (i) cout << ' ';
cout << i % n + 1;
}
cout << endl << endl;
int x;
cin >> x;
} else {
cout << "Second" << endl << endl;
vector<vector<int>> has(n);
for (int i = 0; i < 2 * n; i++) {
int bel;
cin >> bel;
--bel;
has[bel].emplace_back(i);
}
vector<vector<int>> G(2 * n);
for (int i = 0; i < n; i++) {
G[i].emplace_back(i + n);
G[i + n].emplace_back(i);
}
for (int i = 0; i < n; i++) {
G[has[i][0]].emplace_back(has[i][1]);
G[has[i][1]].emplace_back(has[i][0]);
}
vector<int> col(2 * n, -1);
vector<int> zeros, ones;
int res = 0;
function<void(int, int)> go = [&] (int u, int c) {
col[u] = c;
if (c == 0) {
res = (res + u + 1) % (2 * n);
zeros.emplace_back(u);
} else {
ones.emplace_back(u);
}
for (auto& v : G[u]) {
assert(col[v] != col[u]);
if (col[v] == -1) {
go(v, c ^ 1);
}
}
};
for (int i = 0; i < 2 * n; i++) {
if (col[i] == -1) {
go(i, 0);
}
}
if (res == 0) {
for (int i = 0; i < sz(zeros); i++) {
if (i) cout << ' ';
cout << zeros[i] + 1;
}
} else {
for (int i = 0; i < sz(ones); i++) {
if (i) cout << ' ';
cout << ones[i] + 1;
}
}
cout << endl << endl;
int x;
cin >> x;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}