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\) 之后一定会跳过来,假设 \(A\) 下一步就能抓住 \(B\) 了,此时又分情况:
- 若 \(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;
}
重要的是自信,一旦有了自信,人就会赢得一切。