Codeforces Round #668 (Div. 1)题解

自闭场,VP的时候一道题都不会做,我退役罢

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;
}

 

posted @ 2020-11-20 10:29  huangxuanao  阅读(179)  评论(0)    收藏  举报