AtCoder Beginner Contest 396


A - Triple Four

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }
    for (int i = 0; i + 2 < n; ++ i) {
    	if (a[i] == a[i + 1] && a[i + 1] == a[i + 2]) {
    		std::cout << "Yes\n";
    		return;
    	}
    }

    std::cout << "No\n";
}

B - Card Pile

维护一个栈

点击查看代码
void solve() {
    int q;
    std::cin >> q;
    std::stack<int> s;
    for (int i = 0; i < 100; ++ i) {
    	s.push(0);
    }
    while (q -- ) {
    	int t, x;
    	std::cin >> t;
    	if (t == 1) {
    		std::cin >> x;
    		s.push(x);
    	} else {
    		std::cout << s.top() << "\n";
    		s.pop();
    	}
    }
}

C - Buy Balls

题意:给你两个数组,每个数组选一些数,第一个数组选的数的个数要大于等于第二个数组的数。求选出数的最大值。

两个数组从大到小排序,然后维护第二个数组的前缀最大值,第一个数组一直取下去,每个前缀取最大值。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n), b(m);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 0; i < m; ++ i) {
    	std::cin >> b[i];
    }

    std::sort(a.begin(), a.end(), std::greater<int>());
    std::sort(b.begin(), b.end(), std::greater<int>());
    i64 ans = 0, sum = 0, pre = 0;
    for (int i = 0; i < n; ++ i) {
    	sum += a[i];
    	if (i < m) {
    		pre = std::max(pre, pre + b[i]);
    	}

    	ans = std::max(ans, sum + pre);
    }

    std::cout << ans << "\n";
}

D - Minimum XOR Path

题意:求\(1\)\(n\)的路径的最小异或值。

\(n\)很小,直接爆搜。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<std::pair<int, i64>>> adj(n);
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	i64 w;
    	std::cin >> u >> v >> w;
    	-- u, -- v;
    	adj[u].push_back({v, w});
    	adj[v].push_back({u, w});
    }

    i64 ans = (1ll << 60) - 1;
    std::vector<int> st(n);
    auto dfs = [&](auto self, int u, i64 val) -> void {
    	if (u == n - 1) {
    		ans = std::min(ans, val);
    	}

    	for (auto & [v, w] : adj[u]) {
    		if (st[v]) {
    			continue;
    		}

    		st[v] = 1;
    		self(self, v, val ^ w);
    		st[v] = 0;
    	}
    };

    st[0] = 1;
    dfs(dfs, 0, 0);
    std::cout << ans << "\n";
}

E - Min of Restricted Sum

题意:有\(n\)个数,给出\(m\)条限制,\(a_{x_i} \oplus a_{y_i} = z_i\)。求所有合法序列里总和最小的。

对于每一对\(x_i, y_i\)可以用并查集把它们合并到一个集合,维护每个点到根的异或和就行。
那么我们把每个联通块拿出来,给根节点赋值,发现如果\(u\)\(root\)的异或和的第\(i\)位如果是\(1\),那么\(root\)这一位取\(1\)就可以让\(u\)这一位为\(0\),否则\(root\)\(0\)可以让\(u\)这一位为\(0\)。那么统计每一个联通块的点与根的异或和的每一位的\(1\)的个数,如果\(1\)的个数大于等于联通块大小的一半,就给\(root\)这一位赋值\(1\),否则赋值\(0\)

点击查看代码
struct DSU {
	std::vector<int> fa, cnt, val;
	DSU(int _n) {
		init(_n);
	}

	void init(int _n) {
		fa.assign(_n, 0);
		cnt.assign(_n, 1);
		val.assign(_n, 0);
		std::iota(fa.begin(), fa.end(), 0);
	}

	std::pair<int, int> find(int u) {
		if (fa[u] != u) {
            int old = fa[u];
            fa[u] = find(fa[u]).first;  // 路径压缩
            val[u] ^= find(old).second;  // 更新异或值
        }
        return {fa[u], val[u]};
	}

	bool merge(int x, int y, int z) {
		auto [u, val_x] = find(x);
		auto [v, val_y] = find(y);
		if (u == v) {
			if ((val_x ^ val_y) != z) {
				return false;
			}
			return true;
		}

		fa[v] = u;
		cnt[u] += cnt[v];
		val[v] = val_x ^ val_y ^ z;
		return true;
	}

	bool same(int x, int y) {
		return find(x).first == find(y).first;
	}
};

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::array<int, 3>> a(m);
    for (int i = 0; i < m; ++ i) {
    	int x, y, z;
    	std::cin >> x >> y >> z;
    	-- x, -- y;
    	a[i] = {x, y, z};
    }

    DSU dsu(n);
    for (auto & [x, y, z] : a) {
    	if (!dsu.merge(x, y, z)) {
    		std::cout << -1 << "\n";
    		return;
    	}
    }

    std::vector<std::vector<std::pair<int, int>>> g(n);
    for (int i = 0; i < n; ++ i) {
    	auto [u, x] = dsu.find(i);
    	g[u].push_back({i, x});
    }

    std::vector<int> ans(n);
    for (int i = 0; i < n; ++ i) {
    	auto & A = g[i];
    	int k = A.size();
    	std::array<int, 30> cnt{0};
    	for (auto & [x, v] : A) {
    		for (int j = 0; j < 30; ++ j) {
    			if (v >> j & 1) {
    				++ cnt[j];
    			}
    		}
    	}

    	int res = 0;
    	for (int j = 0; j < 30; ++ j) {
    		if (cnt[j] >= k - cnt[j]) {
    			res |= 1 << j;
    		}
    	}

    	for (auto & [x, v] : A) {
    		ans[x] = res ^ v;
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}

F - Rotated Inversions

题意:给你一个数组,对于每个\(k \in [0, m - 1]\),使得\(a_i = (a_ i + k) \% m\)。求这\(k\)个情况的逆序对。

分析一下,对于\(i, j(i < j)\)来说,它们在\(k\)取什么值的情况会产生逆序对。
如果\(a_i > a_j\),则当\(k \in [0, m - a_i)\)\(k \in [m - a_j, m)\)它们产生逆序对。
如果\(a_i < a_j\),则当\(k \in [m - a_j, m - a_i)\)它们产生逆序对。
那么我们可以枚举\(i, j\),然后对它们产生贡献的\(k\)的区间加一,这样就变成了区间加的问题,可以用差分维护。但这样依然是\(n^2\)的。
在差分的角度,我们来看\(j\)什么时候产生\(1\)的贡献。观察上面两种情况,发现对于每个\(i < j, a_i \neq a_j\)的位置,都在差分数组的\(m - a_j\)\(1\),同时对于每个\(i > j, a_i \neq a_j\)的位置加\(-1\)。对于\(a_i > a_j\)\([m - a_j, m)\)这个区间,实际我们不会差分到\(m\)这个地方,所以可以不考虑这个位置应该减去的\(1\)。但对于\([0, m - a_i)\)这个区间,我们在\(m - a_i\)处减了\(1\),那么\(0\)这个位置的\(1\)该如何记录?我们可以先把\(k=0\)的情况的逆序对求出来,那么对于应该给\(0\)这个位置加\(1\)的位置我们就不用管了。然后就可以直接前缀和。

点击查看代码
template <class T>
struct Fenwick {
    int n;
    std::vector<T> tr;

    Fenwick(int _n) {
        init(_n);
    }

    void init(int _n) {
        n = _n;
        tr.assign(_n + 1, T{});
    }

    void add(int x, const T &v) {
        for (int i = x; i <= n; i += i & -i) {
            tr[i] = tr[i] + v;
        }
    }

    T query(int x) {
        T res{};
        for (int i = x; i; i -= i & -i) {
            res = res + tr[i];
        }
        return res;
    }

    T sum(int l, int r) {
        return query(r) - query(l - 1);
    }
};

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    Fenwick<int> tr(m + 1);
    i64 sum = 0;
   	for (int i = 0; i < n; ++ i) {
   		sum += tr.sum(a[i] + 2, m + 1);
   		tr.add(a[i] + 1, 1);
   	}

   	std::vector<i64> ans(m + 1);
   	ans[0] = sum;
   	std::vector<int> cnt(m);
   	for (int i = 0; i < n; ++ i) {
   		ans[m - a[i]] += i - cnt[a[i]];
   		cnt[a[i]] += 1;
   	}

   	std::fill(cnt.begin(), cnt.end(), 0);
   	for (int i = n - 1; i >= 0; -- i) {
   		ans[m - a[i]] -= (n - 1 - i) - cnt[a[i]];
   		cnt[a[i]] += 1;
   	}

   	for (int i = 1; i < m; ++ i) {
   		ans[i] += ans[i - 1];
   	}

   	for (int i = 0; i < m; ++ i) {
   		std::cout << ans[i] << " \n";	
   	}
}
posted @ 2025-03-08 21:40  maburb  阅读(222)  评论(2)    收藏  举报