Codeforces Round #668 (Div. 1)题解
A.Balanced Bitstring
题解:找性质题
注意到如果要使每个长为$k$的子串中$0,1$数量相等,则$a_i=a_{i+k},i \in [1,n-k]$
于是如果存在$a_{i \mod k}$不相等,则无解
这样我们就有了一段长为$k$的字符串
统计它之中$0,1$的个数
如果$>k/2$则无解
否则一定有解
#include <bits/stdc++.h>
using namespace std;
int n, k, sum1, sum2;
string a;
int f[300011];
void solve() {
scanf("%d%d", &n, &k);
cin >> a; memset(f, -1, sizeof(f));
for(int i = 0; i < n; i++) {
if(a[i] != '?') {
if(f[i%k] == -1) f[i % k] = a[i] - '0';
else if(f[i%k] != (int)(a[i] - '0')) {
cout << "NO" << endl;
return;
}
}
}
sum1 = sum2 = 0;
for(int i = 0; i < k; i++) {
sum1 += (f[i] == 0);
sum2 += (f[i] == 1);
}
if(sum1 > k/2 || sum2 > k/2) cout << "NO" << endl;
else cout << "YES" << endl;
}
int main() {
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}
B.Tree Tag
题解:我们考虑什么情况下Alice可以获胜
如果$dis_{a,b} \leq da$,则Alice可以一步就追上Bob
如果Alice处在一个能覆盖整棵树的点,即$da*2+1 \geq$树的直径,那么Bob也无处可走了
其它情况下,Alice会一步一步逼近Bob,并一定能把Bob逼近某棵子树
如果当前Alice占据一个点,使Bob无论怎么走都还在Alice的控制范围内,那么Alice必胜
此时条件即为$2da \geq db$
除以上条件外,Bob获胜
#include <bits/stdc++.h>
using namespace std;
int n, a, b, da, db, dis[100011];
vector <int> e[100011];
void dfs(int w, int fa) {
dis[w] = dis[fa] + 1;
for(int i = 0; i < (int)e[w].size(); i++) {
int ver = e[w][i];
if(ver != fa) dfs(ver, w);
}
}
void solve() {
scanf("%d%d%d%d%d", &n, &a, &b, &da, &db);
for(int i = 1; i <= n; i++) e[i].clear();
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v); e[v].push_back(u);
}
dfs(1, 0); int maxw = 0;
for(int i = 1; i <= n; i++)
if(dis[maxw] < dis[i]) maxw = i;
dfs(maxw, 0);
maxw = 0;
for(int i = 1; i <= n; i++)
if(dis[maxw] < dis[i]) maxw = i;
if(dis[maxw] <= da*2+1) {
printf("Alice\n");
return;
}
dfs(a, 0);
if(dis[b] <= da+1) {
printf("Alice\n");
return;
}
if(2*da >= db) {
printf("Alice\n");
return;
}
printf("Bob\n");
}
int main() {
int t; scanf("%d", &t);
while(t--) solve();
return 0;
}
C.Fixed Point Removal
题解:
注:下文中的$l,r$指可以移除数字的区间,而非输入格式中的$l,r$
移除一个数可以使序列集体左移,但不可能右移,于是$a_i>i$一定不会被移除
考虑暴力,令$num_i$表示$i$之前(含)能被移除的数的个数
则如果$i-num_{i-1}(num_i) \leq a_i \leq i$,则这个数能被删除,$num_i=num_{i-1}+1$,否则$num_i=num_{i-1}$
答案为$num_r$,这样我们就有$O(n^2)$的做法了
#include <bits/stdc++.h>
using namespace std;
int n, q, l, r, a[300011], num[300011];
int main() {
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= q; i++) {
scanf("%d%d", &l, &r);
r = n - r; l++;
num[l-1] = 0;
for(int j = l; j <= r; j++) {
num[j] = num[j-1];
if(j >= a[j] && j - num[j-1] <= a[j]) num[j]++;
}
printf("%d\n", num[r]);
}
return 0;
} //TLE on test 6
如何优化这个做法呢?我们发现对于一个数,它只能被移除一次
于是我们想到了离线,将$l$端点按从大到小排序,依次处理
一个数被移除,会使它后面的数的$num \gets num+1$
上文中的$i-num[i] \leq a[i]$移项得到$i-a[i]-num[i] \leq 0$,我们用线段树维护这个值
每当一个数能被移除,就对后面的值全部$-1$,并查找是否有$\leq 0$的数(最靠左),如果有就重复这个操作
查询时可以再开一个树状数组/线段树,把移除值的位置赋为$1$,再做区间求和即可
因为每个数只会被移除一次,所以复杂度为$O(n \log n)$
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e6;
int n, m, a[300011], num[300011];
struct ques {
int l, r, id, ans;
} q[300011];
bool cmp1(const ques p1, const ques p2) {return p1.l > p2.l;}
bool cmp2(const ques p1, const ques p2) {return p1.id < p2.id;}
struct segment_tree {
int minn[1200011], tag[1200011];
void build(int k, int l, int r) {
minn[k] = inf; tag[k] = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(k*2, l, mid); build(k*2+1, mid+1, r);
}
void pushdown(int k) {
if(tag[k]) {
minn[k*2] += tag[k];
minn[k*2+1] += tag[k];
tag[k*2] += tag[k];
tag[k*2+1] += tag[k];
tag[k] = 0;
}
}
void modify(int k, int l, int r, int w, int s) {
if(l == r) {
minn[k] = s;
return;
}
pushdown(k);
int mid = (l + r) >> 1;
if(w <= mid) modify(k*2, l, mid, w, s);
else modify(k*2+1, mid+1, r, w, s);
minn[k] = min(minn[k*2], minn[k*2+1]);
} //单点赋值
void Modify(int k, int l, int r, int L, int R) {
if(l > R || r < L) return;
if(l >= L && r <= R) {
tag[k]--; minn[k]--;
return;
}
pushdown(k);
int mid = (l + r) >> 1;
Modify(k*2, l, mid, L, R);
Modify(k*2+1, mid+1, r, L, R);
minn[k] = min(minn[k*2], minn[k*2+1]);
} //区间-1
int find(int k, int l, int r) {
if(l == r) {
if(minn[k] <= 0) return l;
return 0;
}
pushdown(k);
int mid = (l + r) >> 1;
if(minn[k*2] <= 0) return find(k*2, l, mid);
else if(minn[k*2+1] <= 0) return find(k*2+1, mid+1, r);
return 0;
}
} t;
struct TA {
int s[300011];
int lowbit(int sum) {
return sum & -sum;
}
void add(int w) {
for(int i = w; i <= n; i += lowbit(i))
s[i]++;
}
int query(int w) {
int ret = 0;
for(int i = w; i; i -= lowbit(i))
ret += s[i];
return ret;
}
} T;
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= m; i++) {
scanf("%d%d", &q[i].l, &q[i].r);
q[i].r = n-q[i].r; q[i].l++; q[i].id = i;
q[i].ans = 0;
}
sort(q+1, q+m+1, cmp1); int w = n+1;
t.build(1, 1, n);
for(int i = 1; i <= m; i++) {
while(w > q[i].l) {
-- w; if(w < a[w]) continue;
t.modify(1, 1, n, w, w - a[w]);
}
int pos;
while(pos = t.find(1, 1, n)) {
T.add(pos);
t.modify(1, 1, n, pos, inf);
t.Modify(1, 1, n, pos+1, n);
}
q[i].ans = T.query(q[i].r);
}
sort(q+1, q+m+1, cmp2);
for(int i = 1; i <= m; i++)
printf("%d\n", q[i].ans);
return 0;
}

浙公网安备 33010602011771号