HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)


黑白可能不适应,右下角可调背景,黑白转换。

C

如果做过最初分块肯定是秒了(我可能一辈子都不会学),如果用线段树做过类似的题也应该知道定义 \(cover[i]\) 表示原来的 \(i\) 现在变成了 \(cover[i]\)

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5, M = 27;

int n, m, cover[100];
char a[N];

int main() {
	cin >> n >> (a + 1);
	for (int i = 0; i < 26; i++) cover[i] = i;
	cin >> m;
	while (m--) {
		char x, y;
		cin >> x >> y;
		for (int i = 0; i < 26; i++) {
			if (cover[i] == x - 'a') cover[i] = y - 'a';
		}
	}
	for (int i = 1; i <= n; i++) cout << char(cover[a[i] - 'a'] + 'a');
}

D

先将所有数写成唯一分解形式,去掉平方因子。

观察到如果 \(d^2=a\times b\),那么 \(a\) 去掉平方因子后和 \(b\) 去掉平方因子的数相等。简单证明:

  • \(a,b\) 都为平方数,去掉平方因子后都为 \(1\)
  • 否则,\(a,b\) 就都不为平方数,如果乘积为平方数,则表示这两个数的奇数次幂因子互补,则奇数次幂都为 \(1\)

那么我们开个桶来存储,计算之前有多少个数去掉平方因子后和该数相等即可。注意特殊处理 \(0\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 3e5 + 5, base = 131;

int n, a[N], vis[N], prime[N];
pair<int, int> tt[N];
int len;

int check(int x) {
	if (x == 2) return 1;
	for (int i = 2; i <= x / i; i++) {
		if (x % i == 0) return 0;
	}
	return 1;
}

signed main() {
	cin >> n;
	int ans = 0;
	for (int i = 1; i <= n; i++) cin >> a[i];
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (a[i] == 0) { // 特殊处理 0 
			ans += n - (cnt + 1);
			cnt++;
			continue;
		}
		int t = a[i]; len = 0;
		for (int j = 2; j <= t / j; j++) {
			if (t % j == 0) {
				int cnt = 0;
				while (t % j == 0) t /= j, cnt++;
				tt[++len] = {j, cnt % 2};
			}
		}
		if (t > 1) tt[++len] = {t, 1};
		int res = 1;
		for (int j = 1; j <= len; j++) {
			if (tt[j].second) res = res * tt[j].first;
		}
		ans += vis[res];
		vis[res]++;
	}
	cout << ans << endl;
}

我的代码肯定不是最简化的,事实上,可以牺牲一点效率,直接枚举所有平方数,让 \(a_i\) 除以他们(当然要求没有余数),就是去掉平方因子后的数。

E

要求出所有 \(i\)\(n\) 的最短路,显然思路是从 \(n\) 开始跑反图。可以搭配 dijkstra 算法。

但是遇到了一个问题,初始 \(n\) 的时间设置为多少?\(0\) 吗?事实上,我们可以将此设置为正无穷。

对于当前点 \(u\),存在反图上的边 \(u\to v\),找到可以从 \(v\) 出发可以到达 \(u\) 最晚的一班火车。首先,如果这趟车 \(l+c>f_u\),那么这趟车是上不去的。否则,我们找到的车次 \(t\) 就是 \(\min\{\lfloor \dfrac{f_u-l-c}{d}\rfloor,k-1\}\)。更新 \(f_v=l+t\times d\)

#include <bits/stdc++.h>
using namespace std;

#define PII pair<int, int>
#define int long long
const int N = 3e5 + 5;

int n, m, dist[N], st[N];

struct node {
	int v, l, d, k, c; 
};
vector<node> G[N];

void dijstra() {
	fill(dist + 1, dist + n + 1, -2e18);
	dist[n] = 2e18;
	priority_queue<PII> q;
	q.push({dist[n], n});
	while (q.size()) {
		auto t = q.top(); q.pop();
		int u = t.second;
		for (auto e : G[u]) {
			int v = e.v, l = e.l, d = e.d, k = e.k, c = e.c;
			if (l + c > dist[u]) continue;
			int D = l + min((dist[u] - l - c) / d, k - 1) * d;
			if (dist[v] < D) {
				dist[v] = D;
				q.push({dist[v], v});
			}
		}
	}
}

signed main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int l, d, k, c, u, v;
		cin >> l >> d >> k >> c >> u >> v;
		G[v].push_back({u, l, d, k, c});
	}
	dijstra();
	for (int i = 1; i < n; i++) {
		if (dist[i] == -2e18) puts("Unreachable");
		else cout << dist[i] << endl;
	}
}

F

定义 \(dp_i\) 表示 \(x\) 当前为 \(i\) 时获胜的概率。

有两种选择,投骰子,停止。则 \(dp_i=\max\{\dfrac{\sum_{j=i+1}^{i+D}dp_j}{D},\text{slove(i)}\}\)。其中 \(\text{slove(i)}\) 表示 \(x\)\(i\) 的时候停止操作,\(y\) 接下来操作一波后,获胜的概率。

\(g_i\) 表示最后 \(y\)\(i\) 的概率,\(sum_i\) 表示 \(g_i\) 的前缀和。那么 \(\text{slove(i)}\) 有两种情况能赢:

  • 如果 \(y\) 越界了 \(y>N\),则获胜,概率为 \(1-sum_N\)
  • 如果 \(y<x\),则获胜,概率为 \(sum_{i-1}\)

那么 \(g\) 数组如何计算?初始时 \(g_0=1\),代码说可能更直接:

g[0] = 1.0;
for (int i = 0; i <= MAXN; i++) {
	if (i < L) {
		for (int j = i + 1; j <= i + D; j++) g[j] += g[i] / D;
		g[i] = 0;
	}
} 

直接这样做会超时,可以用线段树维护,单点查询,区间修改。

代码

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 4e5 + 5;

int n, l, d;
double g[N], dp[N], sum[N];

struct fenwick {
	double c[N][2];
	void mo(int x, double v) {
		x += 2;
		for (int i = x; i <= N - 5; i += i & -i) {
			c[i][0] += v;
			c[i][1] += x * v;
		}
	}
	void modify(int l, int r, double v) {
		mo(l, v);
		mo(r + 1, -v);
	}
	double get_sum(int op, int x) {
		x += 2;
		double res = 0.0;
		for (int i = x; i; i -= i & -i) res += c[i][op];
		return res;
	}
	double query(int l, int r) {
		double t1 = get_sum(0, l - 1) * 1.0 * (l + 2) - get_sum(1, l - 1);
		double t2 = get_sum(0, r) * (1.0 * r + 3) - get_sum(1, r);
		return t2 - t1;
	}
}tr;

double slove(int x) {
	if (x > n) return 0.0;
	double res = 1 - g[n];
	if (x >= 1) res += g[x - 1];
	return res;
}

signed main() {
	cin >> n >> l >> d;
	g[0] = 1.0; tr.modify(0, 0, 1.0);
	for (int i = 0; i <= N - 5; i++) {
		double t = tr.query(i, i); g[i] = t;
		if (i < l) {
			tr.modify(i + 1, i + d, t / d);
			g[i] = 0.0;
		}
	}
//	for (int i = 0; i <= 10; i++) cout << g[i] << ' ';
//	puts("");
	for (int i = 1; i <= N - 5; i++) g[i] += g[i - 1];
	for (int i = N - 5; i >= 0; i--) {
		if (i > n) dp[i] = 0.0;
		else dp[i] = max((sum[i + 1] - sum[i + d + 1]) / d, slove(i));
		sum[i] = sum[i + 1] + dp[i];
	}
	printf("%.10lf", dp[0]);
}

G

线段树套 multiset。对于区间 \([l,r]\) 执行 \(\max\{a_i,x\}\) 操作来讲,我们 \([l,r]\) 包含的线段树区间内,将 multiset 中加入 \(x\)

删除第 \(i\) 次,只需要记录第 \(i\) 个操作的 \(l_i,r_i,x_i\),仿照上面的操作,只需要将 multiset 中删除 \(x_i\)

查询第 \(x\) 个元素,在线段树递归过程中,取所有 multiset 的最大数。

#include <bits/stdc++.h>
using namespace std;

#define ls p << 1
#define rs p << 1 | 1
#define int long long
const int N = 3e5 + 5;

int n, a[N], l[N], r[N], v[N], idx;

struct edge {
	int l, r;
	multiset<int> s;
}tree[N * 4];

void build(int p, int l, int r) {
	tree[p].l = l, tree[p].r = r;
	if (l == r) {
		tree[p].s.insert(a[l]);
		return;
	}
	int mid = (l + r) >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r); 
}

void modify(int op, int p, int l, int r, int x) {
	if (l <= tree[p].l && tree[p].r <= r) {
		if (op == 0) tree[p].s.insert(x);
		else tree[p].s.erase(tree[p].s.find(x));
		return;
	} 
	int mid = (tree[p].l + tree[p].r) >> 1; 
	if (l <= mid) modify(op, ls, l, r, x);
	if (r > mid) modify(op, rs, l, r, x);
}

int query(int p, int x) {
	if (tree[p].l == tree[p].r) {
		if (tree[p].s.size() == 0) return 0;
		return (*(--tree[p].s.end()));
	}
	int mid = (tree[p].l + tree[p].r) >> 1, res = 0;
	if (tree[p].s.size()) res = (*(--tree[p].s.end()));
	if (x <= mid) res = max(res, query(ls, x));
	else res = max(res, query(rs, x));
	return res;
}

signed main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	build(1, 1, n);
	int m; cin >> m;
	while (m--) {
		int op, x;
		cin >> op;
		idx++;
		if (op == 1) {
			cin >> l[idx] >> r[idx] >> v[idx];
			modify(0, 1, l[idx], r[idx], v[idx]);
		} 
		if (op == 2) {
			cin >> x;
//			cout << "debug=" << l[x] << ' ' << r[x] << ' ' << v[x] << endl;
			modify(1, 1, l[x], r[x], v[x]);
		}
		if (op == 3) {
			cin >> x;
			cout << query(1, x) << endl;
		}
	} 
}
posted @ 2024-02-25 19:44  Otue  阅读(21)  评论(0编辑  收藏  举报