Atcoder Beginner Contest 424

A

按照题意判断即可。

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

int a, b, c;

signed main() {
	scanf ("%lld %lld %lld", &a, &b, &c);
	if (a == b || b == c || a == c) 
		printf ("Yes\n");
	else
		printf ("No\n");
	return 0;
}

B

用一个桶记每个人通过的题数即可。

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

int n, m, K;
int a[15], b[15];
int cnt[15];
bool used[15];
vector<int> ans;

signed main() {
	scanf ("%lld", &n);
	scanf ("%lld", &m);
	scanf ("%lld", &K);
	for (int i = 1; i <= K; i++) {
		int a, b;
		scanf ("%lld %lld", &a, &b);
		cnt[a]++;
		if (cnt[a] == m && !used[a])
			ans.push_back(a), used[a] = true;
	}
	for (int id : ans)
		printf ("%lld ", id);
	return 0;
	
}

C

考虑建有向图跑 dfs 即可,答案即为访问到的点的个数。

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

const int N = 2e5 + 10;
int n, a[N], b[N];
int head[N], idx, ans;
bool vis[N];

struct GRAPH {
	int v;
	int nxt;
} edge[N << 1];

inline void addedge(int u, int v) {
	edge[idx].v = v;
	edge[idx].nxt = head[u];
	head[u] = idx++;
}

void dfs(int u) {
	if (vis[u]) return;
	vis[u] = true;
	for (int i = head[u]; ~i; i = edge[i].nxt) dfs(edge[i].v);
}

signed main() {
	scanf ("%lld", &n);
	memset (head, -1, sizeof(head));
	for (int i = 1; i <= n; i++) {
		scanf ("%lld %lld", &a[i], &b[i]);
		addedge(a[i], i), addedge(b[i], i);
	}
	for (int i = 1; i <= n; i++) {
		if (!a[i] && !b[i]) dfs(i);
	}
	for (int i = 1; i <= n; i++)
		ans += vis[i];
	printf ("%lld\n", ans);
	return 0;
}

D

\(H,W \leq 7\),这不明显状压吗,诶我不会了啊啊啊啊

考虑直接 dfs,一行一行的搜。每次的最优操作一定是染白下一行的两个点,分别染白后继续递归即可。如果当前 \(2\times 2\) 内有白点就不用操作。

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

int Test;
int h, w, ans;
char s[10][10];

void dfs(int x, int y, int step) {
	if (step > ans) return;
	if (y == w) dfs(x + 1, 1, step);
	if (x == h) {
		ans = min(ans, step);
		return;
	}
	
	if (s[x][y] == '.' || s[x + 1][y] == '.' || s[x][y + 1] == '.' || s[x + 1][y + 1] == '.') dfs(x, y + 1, step);
	else {
		s[x + 1][y] = '.';
		dfs(x, y + 1, step + 1);
		s[x + 1][y] = '#';
		s[x + 1][y + 1] = '.';
		dfs(x, y + 1, step + 1);
		s[x + 1][y + 1] = '#';
	}
}

void sol() {
	scanf ("%lld", &h);
	scanf ("%lld", &w);
	ans = h * w;
	for (int i = 1; i <= h; i++)
		scanf ("%s", s[i] + 1);
	dfs (1, 1, 0);
	printf ("%lld\n", ans);
}

signed main() {
	scanf ("%lld", &Test);
	for (int _ = 1; _ <= Test; _++) sol();
	return 0;
}

E

读完题第一反应是优先队列直接维护,猜到有些细节。

最暴力最直接容易想到的方法是 \(O(K)\) 暴力维护,显然不可取。

但是这也启发我们如何优化,注意到 \(O(K)\) 最大的问题是最大 \(A_i\) 一个一个处理。其实我们记二元组 \((val, cnt)\) 分别表示值和对应的出现次数。每次折半等效于 \((val, cnt) \rightarrow (\frac{val}{2}, cnt \times 2)\)。但是值得注意的,当最后一次操作时 \(cnt > K\) 的情况,那么最多会是 \((\frac{val}{2}, K \times 2)\) 表示已经折半操作了的,还会剩下 \((val, cnt - K)\) 没折半操作的。

最后每次 \(X \leftarrow X - cnt\) 暴力找答案即可。复杂度 \(O(n \log K \log n)\)

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

const int N = 1e5 + 10;
int Test;
int n, K, X;
long double a[N], ans;
map<long double,int> cnt;
priority_queue<pair<long double,int>> q;

void init() {
	cnt.clear(), ans = 0;
	while (!q.empty()) q.pop();
}

void sol() {
	init();
	scanf ("%lld", &n);
	scanf ("%lld", &K);
	scanf ("%lld", &X);
	for (int i = 1; i <= n; i++)
		scanf ("%Lf", &a[i]), cnt[a[i]]++;
	for (auto it = cnt.begin(); it != cnt.end(); it++)
		q.push(make_pair(it -> first, it -> second));
	while (K > 0) {
		auto t = q.top();
		q.pop();
		if (K < t.second) {
			q.push(make_pair(t.first, t.second - K));
			q.push(make_pair(t.first / 2, K * 2));
			K = 0;
			break;
		}
		else {
			K -= t.second;
			q.push(make_pair(t.first / 2, t.second * 2));
		}
	}
	while (X > 0) {
		auto t = q.top();
		q.pop();
		if (X - t.second <= 0) {
			X = 0;
			ans = t.first;
			break;
		}
		X -= t.second;
	}
	printf ("%.15Lf\n", ans);
}

signed main() {
	scanf ("%lld", &Test);
	for (int _ = 1; _ <= Test; _++) sol();
	return 0;
}

F

用 GeoGeBra 花了 1mol 分钟的图

分别花相交和不交的情况,再展开成链可以得到一个神奇的规律:相交的线段在链上是有交但不包含的区间,不交的线段在链上是包含或不交的区间。考虑区间数据结构维护,[选择线段树]。

那我们该如何判断以上三种区间之间情况呢?

我们意识到我们并不需要维护仍和区间上的权值,只需要考虑下标。所以说有交并不包含就可以通过以与当前区间有交区间的最大右端点大于当前右端点或最小的左端点小于当前左端点来判断。那么我们就可以开两棵线段树分别维护以 \(\forall i\) 为左端点的最大右端点和以 \(\forall i\) 为右端点的最小左端点来解决。

#include <bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e6 + 10;
int n, q;
int T1[N << 2], T2[N << 2];
// T1 record max value, T2 record min value.

inline void pushup1(int rt) {
    T1[rt] = max(T1[lson], T1[rson]);
}

inline void pushup2(int rt) {
    T2[rt] = min(T2[lson], T2[rson]);
}

void build1(int rt, int l, int r) {
    if (l == r) {
        T1[rt] = ~inf;
        return;
    }
    int mid = (l + r) / 2;
    build1(lson, l, mid);
    build1(rson, mid + 1, r);
    pushup1(rt);
}

void build2(int rt, int l, int r) {
    if (l == r) {
        T2[rt] = inf;
        return;
    }
    int mid = (l + r) / 2;
    build2(lson, l, mid);
    build2(rson, mid + 1, r);
    pushup2(rt);
}

void update(int rt, int qpos, int l, int r, int val) {
    if (l == r) {
        T1[rt] = T2[rt] = val;
        return;
    }
    int mid = (l + r) / 2;
    if (qpos <= mid)
        update(lson, qpos, l, mid, val);
    else
        update(rson, qpos, mid + 1, r, val);
    pushup1(rt), pushup2(rt);
}

int query1(int rt, int ql, int qr, int l, int r) {
    if (ql <= l && qr >= r) 
        return T1[rt];
    int mid = (l + r) / 2, res = ~inf;
    if (ql <= mid)
        res = max(res, query1(lson, ql, qr, l, mid));
    if (qr > mid)
        res = max(res, query1(rson, ql, qr, mid + 1, r));
    return res;
}

int query2(int rt, int ql, int qr, int l, int r) {
    if (ql <= l && qr >= r)
        return T2[rt];
    int mid = (l + r) / 2, res = inf;
    if (ql <= mid)
        res = min(res, query2(lson, ql, qr, l, mid));
    if (qr > mid)
        res = min(res, query2(rson, ql, qr, mid + 1, r));
    return res;
}

signed main() {
    scanf ("%lld", &n);
    scanf ("%lld", &q);
    build1 (1, 1, n), build2 (1, 1, n);
    for (int _ = 1; _ <= q; _++) {
        int a, b, l, r;
        scanf ("%lld %lld", &a, &b);

        l = a + 1, r = b - 1;
        if (l <= r) {
            int maxx = query1(1, l, r, 1, n), minn = query2(1, l, r, 1, n);
            if (minn < a || maxx > b) 
                printf ("No\n");
            else {
                update(1, a, 1, n, b);
                update(1, b, 1, n, a);
                printf ("Yes\n");
            }
        }
        else {
            printf ("Yes\n");
            update(1, a, 1, n, b);
            update(1, b, 1, n, a);
        }
    }
    return 0;
}
posted @ 2025-10-10 21:24  xAlec  阅读(11)  评论(0)    收藏  举报