AtCoder Regular Contest 119 VP 记录

赛时半小时过 ABC,最后和核堆分析出 E 来了,不过我赛时没写完。算是做的比较顺的一场 ARC。

A - 119 × 2^23 + 1

给你一个数 \(n\),找到最小的 \(a+b+c\) 满足 \(a \times 2^b + c = n\)

显然,枚举一个 \(b\),然后 \(a\) 越大越优,所以 \(a = \left \lfloor \frac{n}{2^b} \right \rfloor, c = n - \left \lfloor \frac{n}{2^b} \right \rfloor \times 2^b\)

对所有情况取个 \(\min\) 即可。

signed main() {
	n = read();
	ans = n;
	for(int i = 1, p = 0; i <= n; i <<= 1, ++p) {
		ans = min(ans, n / i + n % i + p);
	}
	cout << ans << "\n";
	return 0;
}

B - Electric Board

给你两个长度为 \(n\) 的字符串 \(s,t\),问能否由 \(s\) 变为 \(t\)

变换规则是:选择一个 \(0\) 和相邻的若干个 \(1\) 交换位置。例如 \(0111 \to 1110\)

有无解是两个字符串 \(0,1\) 个数不同。

答案是两个字符串有多少位置不同。

为什么呢,每次贪心的移动,都至少会让一个 \(0\) 相对应。

signed main() {
	n = read();
	cin >> s + 1 >> t + 1;
	for(int i = 1; i <= n; ++i) if(s[i] == '0') a[++topa] = i;
	for(int i = 1; i <= n; ++i) if(t[i] == '0') b[++topb] = i;
	if(topa != topb) return puts("-1"), 0;
	int ans = 0;
	for(int i = 1; i <= topa; ++i) {
		if(a[i] != b[i]) ans ++;
	}
	cout << ans << "\n";
	return 0;
}

C - ARC Wrecker 2

给你一个长度为 \(n\) 的序列。有两种操作:

  • 一对相邻的位置同时 \(+1\)
  • 一对相邻的位置同时 \(-1\)

问有多少区间 \([l,r]\) 经过一定变化后可以变成 \(0\)

发现如果把奇数位和偶数为分开,那么每次操作,两个位数的和只差不会发生变化。

所以只需要统计一下奇偶位上的和是否相同就可以了

奇数位为正,偶数位为负。

前缀和,套上 map,随便做。

signed main() {
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = 1; i <= n; ++i) if(i & 1) a[i] = - a[i];
	Map[0] ++;
	for(int i = 1; i <= n; ++i) {
		a[i] = a[i - 1] + a[i];
		ans += Map[a[i]];
		Map[a[i]] ++;
	}
	cout << ans << "\n";
	return 0;
}

D - Grid Repainting 3

给你一个 \(n \times m\) 的表格。每个位置有红和蓝两种颜色,每次可以选择一个红色颜色的位置,将其所在位置的一行或者一列变成白色。问最后最多能将多少位置变成白色。

\(1 \le n, m \le 2500\)

让我们把每行每列看作一个点,然后如果 \((i,j)\) 这个位置是红色,那么第 \(i\) 行的点和第 \(j\) 列的点连边。

那么对于一个连通块,每次操作就是选择一个点,删掉与它相连的所有边。

显然对于任意一个连通块,最后一定能被删的只剩下一个点,因为可以构造出它的任意一个生成树,然后从度为 \(1\) 的点不断删,最后只剩一个点,而且剩的这个点是行点还是列点还都可以。

进一步分析,对于所有连通块,发现我们最后剩下的点,一定都是一种颜色。

所以,用 dfs 确定一下删的顺序就好啦。

#include<bits/stdc++.h>
#define LL long long 
//#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 2555;
const int INF = 1e9 + 7;
const int mod = 998244353;

int read() {
	int s = 0, f = 0; char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

struct node {
	int opt, x, y;
}ans[MAXN * MAXN];

int n, m, top = 0;
char s[MAXN][MAXN];
vector<int> E[MAXN << 1];
bool vis[MAXN << 1];

void dfs(int u) {
	vis[u] = true;
	for(auto v : E[u]) {
		if(vis[v]) continue;
		dfs(v);
		if(u <= n) ans[++top] = (node){0, u, v - n};
		else ans[++top] = (node){1, v, u - n};
	}
}

signed main() {
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) cin >> s[i] + 1;
	int cnt0 = n, cnt1 = m;
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) {
			if(s[i][j] != 'R') continue;
			if(!E[i].size()) cnt0--;
			if(!E[n + j].size()) cnt1--;
			E[i].push_back(n + j), E[n + j].push_back(i);
		}
	}
	if(cnt0 > cnt1) {
		for(int i = 1; i <= n; ++i) if(!vis[i]) dfs(i);
	} else {
		for(int i = n + 1; i <= n + m; ++i) if(!vis[i]) dfs(i);
	}
	printf("%d\n", top);
	for(int i = 1; i <= top; ++i) {
		printf("%c %d %d\n", ans[i].opt ? 'X' : 'Y', ans[i].x, ans[i].y);
	}
	return 0;
}

E - Pancakes

给你一个长度为 \(n\) 的序列,反转一段区间,使得 \(\sum_{i=2}^{n} |a_i - a_{i-1}|\) 最小。

\(n \le 3 \times 10^5\)

发现操作最多只会改变两个位置的差值。

考虑什么时候答案会更优?设交换的区间是 \([l,r]\)

假设 \(a_{l-1} < a_{l}, a_r < a_{r+1}\)

发现只有 \([a_{l-1},a_l]\)\([a_r, a_{r+1}]\) 有交集的时候,会让答案减少 \(2|S|\),其中 \(|S|\) 指的是交集的大小。

然后就简单了,按照 \(a_{i-1} < a_{i}\)\(a_{i-1} > a_i\) 分类。

对每一类求一下里面最大的两个集合的交即可。

对于交换的区间是 \([1,r], [l,n]\) 的情形可以拿出来单独处理。

对于线段集合的最大交,这是一个比较经典的问题。

可以对里面的线段按照左端点排序,先处理出一条线段被另一条线段包含的情况。

然后就是在剩下的线段中取相邻两个线段的交的最大值。

#include<bits/stdc++.h>
#define LL long long 
#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 4e5 + 10;
const int INF = 1e9 + 7;
const int mod = 998244353;

struct node { int l, r; };
int n, ans = 0;
int a[MAXN];
vector<node> b, c;
int stc[MAXN];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

bool cmp(node x, node y) { return x.l < y.l; }

int Solve(vector<node> a) {
	sort(a.begin(), a.end(), cmp);
	int res = 0, sc = 0, M = a.size();
	for(int i = 0; i < M; ++i) {
		if(sc && a[stc[sc]].r >= a[i].r) res = max(res, a[i].r - a[i].l);
		else stc[++sc] = i;
	}
	for(int i = 0; i < sc; ++i) a[i] = a[stc[i + 1]];
	for(int i = 1; i < sc; ++i) 
		if(a[i].l <= a[i - 1].r) 
			res = max(res, a[i - 1].r - a[i].l);
	return res;
}

signed main() {
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = 2; i <= n; ++i) ans += abs(a[i] - a[i - 1]);
	for(int i = 2; i <= n; ++i) {
		if(a[i - 1] <= a[i]) b.push_back((node){a[i - 1], a[i]});
		if(a[i - 1] >= a[i]) c.push_back((node){a[i], a[i - 1]});
	}
	int del = 0;
	for(int i = 2; i <= n; ++i) {
		del = min(del, abs(a[i] - a[1]) - abs(a[i] - a[i - 1]));
		del = min(del, abs(a[n] - a[i - 1]) - abs(a[i] - a[i - 1]));
	}
	int res1 = Solve(b), res2 = Solve(c);
	ans = min(ans - 2 * max(res1, res2), ans + del);
	printf("%lld\n", ans);
	return 0;
}
posted @ 2022-05-03 16:58  Suzt_ilymtics  阅读(38)  评论(0编辑  收藏  举报