VP Educational Codeforces Round 31


A. Book Reading

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

    for (int i = 0; i < n; ++ i) {
    	t -= 86400 - a[i];
    	if (t <= 0) {
    		std::cout << i + 1 << "\n";
    		return;
    	}
    }
}

B. Japanese Crosswords Strike Back

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

    for (int i = 0; i < n; ++ i) {
    	x -= a[i] + 1;
    }

    ++ x;
    if (x != 0) {
    	std::cout << "NO\n";
    } else {
    	std::cout << "YES\n";
    }
}

C. Bertown Subway

题意:\(p_i\)\(i\)连边,每个点都有一个入度和一个出度。你可以交换一次\(p_i, p_j\)。最后结果就是每个联通块的个数的平方之和。

直接找环,按环里点的个数排序,把最大和次大合并就行。

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

    std::vector<int> st(n);
    std::vector<int> a;
    for (int i = 0; i < n; ++ i) {
    	if (!st[i]) {
    		int j = i;
    		int cnt = 0;
    		while (!st[j]) {
    			++ cnt;
    			st[j] = 1;
    			j = p[j];
    		}

    		a.push_back(cnt);
    	}
    }

    if (a.size() <= 1) {
    	std::cout << (i64)n * n << "\n";
    } else {
	    std::sort(a.begin(), a.end());
    	i64 ans = 0;
    	int m = a.size();
    	for (int i = 0; i + 2 < m; ++ i) {
    		ans += (i64)a[i] * a[i];
    	}

    	ans += (i64)(a[m - 1] + a[m - 2]) * (a[m - 1] + a[m - 2]);
    	std::cout << ans << "\n";
    }
}

D. Boxes And Balls

题意:有\(n\)个球,每个球有\(a_i\)个。每次丢掉两类或者一类球,每次操作都要消耗当前拥有的球的总和。求只剩一类球时的最小花费。

如果反过来考虑,就变成了每次选两个或者三个合并,求最小。
那么就是经典的贪心问题,根据哈夫曼树,我们应该每次选最小的三个合并,如果\(n\)是偶数,应该加一个零进去。

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

    std::priority_queue<i64, std::vector<i64>, std::greater<i64>> heap;
    for (int i = 0; i < n; ++ i) {
    	heap.push(a[i]);
    }

    if (n % 2 == 0) {
    	heap.push(0);
    }

    i64 ans = 0;
    while (heap.size() > 1) {
    	i64 x = heap.top(); heap.pop();
    	i64 y = heap.top(); heap.pop();
    	i64 z = heap.top(); heap.pop();
    	ans += x + y + z;
    	heap.push(x + y + z);
    }

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

E. Binary Matrix

题意:给你一个\(n\times m\)\(01\)矩阵,求其中\(1\)的联通块的个数。但空间只有\(16mb\)

显然无法把矩阵存下来,只能一行一行处理。
考虑使用并查集维护联通块,我们记录上一行每个\(1\)所在的联通块,然后对于当前行的每个\(1\),首先算它们都单独在一个联通块,然后每次和左边已经上一行相同的位置合并,每合并一次联通块数就减一。

点击查看代码
const int N = 1 << 14;

int fa[N << 1];
int last[N], cur[N];

int find(int x) {
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int merge(int x, int y) {
	x = find(x), y = find(y);
	if (x == y) {
		return 0;
	}

	fa[y] = x;
	return 1;
}

void solve() {
    int n, m;
    std::cin >> n >> m;
    auto get = [&](char c) -> int {
    	if (c >= 'A') {
    		return c - 'A' + 10;
    	}

    	return c - '0';
    };

    for (int i = 0; i < 2 * m; ++ i) {
    	fa[i] = i;
    }

    int ans = 0;
    while (n -- ) {
    	std::string s;
    	std::cin >> s;
    	for (int i = 0; i < s.size(); ++ i) {
    		int x = get(s[i]);
    		for (int j = 0; j < 4; ++ j) {
    			cur[i * 4 + 3 - j] = x >> j & 1;
    		}
    	}

    	int sum = 0;
    	for (int i = 0; i < m; ++ i) {
    		sum += cur[i];
    	}

    	for (int i = 1; i < m; ++ i) {
    		if (cur[i] && cur[i - 1]) {
    			sum -= merge(i, i - 1);
    		}
    	}

    	for (int i = 0; i < m; ++ i) {
    		if (cur[i] && last[i]) {
    			sum -= merge(i, i + m);
    		}
    	}

    	ans += sum;
    	for (int i = 0; i < m; ++ i) {
    		last[i] = cur[i];
    	}

    	for (int i = 0; i < m; ++ i) {
    		fa[i + m] = fa[i] + m;
    		fa[i] = i;
    	}
    }

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

F. Anti-Palindromize

题意:给你一个字符串,你要从重排它,使得\(i \in [1, n]\)都有\(s_i \neq s_{n-i+1}\)。设重排后的字符串为\(t\)最终价值为所有\(s_i = t_i\)\(i\)\(b_i\)的和。

考虑费用流。
对于每个字符,源点向他连出现次数的流量0的费用的边。那么这个字符在每个位置都可以放,但不能同时放在\(i\)\(n-i+1\),那么我们可以只向前\(\frac{n}{2}\)个位置连边,同时这些位置往后面对应的位置连流量为1的边。这样一个字符在这两个位置上就只会放一个。同时讨论一下第\(i\)个位置放这个字符的费用。最后每个位置向汇点连流量为1费用为0的边。

点击查看代码
template<class T>
struct MinCostFlow {
    struct _Edge {
        int to;
        T cap;
        T cost;
        _Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {}
    };
    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<T> h, dis;
    std::vector<int> pre;
    bool dijkstra(int s, int t) {
        dis.assign(n, std::numeric_limits<T>::max());
        pre.assign(n, -1);
        std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> que;
        dis[s] = 0;
        que.emplace(0, s);
        while (!que.empty()) {
            T d = que.top().first;
            int u = que.top().second;
            que.pop();
            if (dis[u] != d) {
                continue;
            }
            for (int i : g[u]) {
                int v = e[i].to;
                T cap = e[i].cap;
                T cost = e[i].cost;
                if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) {
                    dis[v] = d + h[u] - h[v] + cost;
                    pre[v] = i;
                    que.emplace(dis[v], v);
                }
            }
        }
        return dis[t] != std::numeric_limits<T>::max();
    }
    MinCostFlow() {}
    MinCostFlow(int n_) {
        init(n_);
    }
    void init(int n_) {
        n = n_;
        e.clear();
        g.assign(n, {});
    }
    void addEdge(int u, int v, T cap, T cost) {
        g[u].push_back(e.size());
        e.emplace_back(v, cap, cost);
        g[v].push_back(e.size());
        e.emplace_back(u, 0, -cost);
    }
    std::pair<T, T> flow(int s, int t) {
        T flow = 0;
        T cost = 0;
        h.assign(n, 0);
        while (dijkstra(s, t)) {
            for (int i = 0; i < n; ++i) {
                h[i] += dis[i];
            }
            T aug = std::numeric_limits<int>::max();
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                aug = std::min(aug, e[pre[i]].cap);
            }
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                e[pre[i]].cap -= aug;
                e[pre[i] ^ 1].cap += aug;
            }
            flow += aug;
            cost += aug * h[t];
        }
        return std::make_pair(flow, cost);
    }
    struct Edge {
        int from;
        int to;
        T cap;
        T cost;
        T flow;
    };
    std::vector<Edge> edges() {
        std::vector<Edge> a;
        for (int i = 0; i < e.size(); i += 2) {
            Edge x;
            x.from = e[i + 1].to;
            x.to = e[i].to;
            x.cap = e[i].cap + e[i + 1].cap;
            x.cost = e[i].cost;
            x.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};

void solve() {
    int n;
    std::cin >> n;
    std::string S;
    std::cin >> S;
    std::vector<int> b(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> b[i];
    }

    const int N = n + 26 + 26 * n + 2;
    MinCostFlow<int> f(N);
    int s = N - 1, t = N - 2;
    std::vector<int> cnt(26);
    for (auto & c : S) {
    	++ cnt[c - 'a'];
    }

    for (int i = 0; i < 26; ++ i) {
    	f.addEdge(s, n + i, cnt[i], 0);
    	for (int j = 0; j < n / 2; ++ j) {
    		int x = i == S[j] - 'a' ? b[j] : 0, y = i == S[n - 1 - j] - 'a' ? b[n - 1 - j] : 0;
    		f.addEdge(n + i, n + 26 + i * n + j, 1, 0);
    		f.addEdge(n + 26 + i * n + j, n + 26 + i * n + n - 1 - j, 1, 0);
    		f.addEdge(n + 26 + i * n + j, j, 1, -x);
    		f.addEdge(n + 26 + i * n + n - 1 - j, n - 1 - j, 1, -y);
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	f.addEdge(i, t, 1, 0);
    }

    auto [flow, cost] = f.flow(s, t);
    std::cout << -cost << "\n";
}
posted @ 2025-03-12 15:44  maburb  阅读(11)  评论(0)    收藏  举报