VP Panasonic Programming Contest 2024(AtCoder Beginner Contest 375)


A - Seats

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    int ans = 0;
    for (int i = 1; i + 1 < n; ++ i) {
    	if (s[i - 1] == '#' && s[i] == '.' && s[i + 1] == '#') {
    		++ ans;
    	}
    }

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

B - Traveling Takahashi Problem

模拟

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

    auto get = [&](std::pair<int, int> & a, std::pair<int, int> & b) -> double {
    	double dx = a.first - b.first, dy = a.second - b.second;
    	return std::sqrt(dx * dx + dy * dy);
    };

    double ans = 0;
    for (int i = 0; i < n; ++ i) {
    	ans += get(a[i], a[(i + 1) % n]);
    }

    std::cout << std::fixed << std::setprecision(12);
    std::cout << ans << "\n";
}

C - Spiral Rotation

题意:给你一个矩阵,对于\(i \in [1, \frac{n}{2}]\),都进行一次操作,对于每个\(x, y \in [i, n - i + 1]\),使得\(s[y][n - x + 1] = s[x][y]\)。求操作后的矩阵。

发现每次操作到的位置都是旋转\(90\)度。那么每旋转\(4\)次就会转回来,于是把旋转次数\(i\)改为$i % 4%模拟即可。

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

    auto t = s;
    auto rotate = [&](int l, int r) -> void {
    	for (int i = l; i <= r; ++ i) {
    		t[i - 1][n - l] = s[l - 1][i - 1];
    		t[i - 1][n - r] = s[r - 1][i - 1];
    	}

    	for (int i = l + 1; i < r; ++ i) {
    		t[l - 1][n - i] = s[i - 1][l - 1];
    		t[r - 1][n - i] = s[i - 1][r - 1];
    	}

    	for (int i = l; i <= r; ++ i) {
    		s[i - 1][n - l] = t[i - 1][n - l];
    		s[i - 1][n - r] = t[i - 1][n - r];
    	}

    	for (int i = l + 1; i < r; ++ i) {
    		s[l - 1][n - i] = t[l - 1][n - i];
    		s[r - 1][n - i] = t[r - 1][n - i];
    	}
    };

    for (int i = 1; i <= n / 2; ++ i) {
    	for (int k = 0; k < i % 4; ++ k) {
    		rotate(i, n - i + 1);
    	}
    }

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

D - ABA

题意:求一个字符串有多少长度为\(3\)的子序列是回文。

枚举中间点,然后预处理前缀每个字符的数量和后缀每个字符的数量。每个字符的方案数加起来。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    std::vector<std::array<int, 26>> pre(n + 1), suf(n + 2);
    std::fill(pre[0].begin(), pre[0].end(), 0);
    std::fill(suf[n + 1].begin(), suf[n + 1].end(), 0);
    for (int i = 1; i <= n; ++ i) {
    	pre[i] = pre[i - 1];
    	pre[i][s[i - 1] - 'A'] += 1;
    }

    for (int i = n; i >= 1; -- i) {
    	suf[i] = suf[i + 1];
    	suf[i][s[i - 1] - 'A'] += 1;
    }

    i64 ans = 0;
    for (int i = 1; i <= n; ++ i) {
    	for (int j = 0; j < 26; ++ j) {
    		ans += (i64)pre[i - 1][j] * suf[i + 1][j];
    	}
    }

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

E - 3 Team Division

题意:\(n\)个人分为\(3\)类,每个人有\(b_i\)的能力。你要更改尽量少的人的类型,使得每一类的人的能力之和相同。

首先\(b\)的总和得是\(3\)的倍数。
然后记\(f[i][j][k]\)为前\(i\)个人,第一类总和为\(j\)第二类总和为\(k\)第三类总和为\(sum_b - j - k\)的最小更改数。然后枚举当前这个人是哪一类转移即可。要注意如果有个人的能力大于\(\frac{sum_b}{3}\)也是无解。

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

    int m = std::accumulate(b.begin(), b.end(), 0);

    if (m % 3) {
    	std::cout << -1 << "\n";
    	return;
    }

    const int inf = 1e9;
    std::vector f(m + 1, std::vector<int>(m + 1, inf));
    f[0][0] = 0;
    int sum = 0;
    for (int i = 0; i < n; ++ i) {
	    std::vector g(m + 1, std::vector<int>(m + 1, inf));
	    for (int x = 0; x <= sum; ++ x) {
	    	for (int y = 0; x + y <= sum; ++ y) {
	    		int z = m - x - y;
	    		g[x + b[i]][y] = std::min(g[x + b[i]][y], f[x][y] + (a[i] != 1));
	    		g[x][y + b[i]] = std::min(g[x][y + b[i]], f[x][y] + (a[i] != 2));
	    		g[x][y] = std::min(g[x][y], f[x][y] + (a[i] != 3));
	    	}
	    }

	    sum += b[i];
	    f = g;
    }

    int ans = f[m / 3][m / 3] == inf ? -1 : f[m / 3][m / 3];

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

F - Road Blocked

题意:\(n\)个点\(m\)条边。求全源最短路。每次有两个操作,有个数删掉第\(i\)条边,一个是询问\(x, y\)之间的最短路。

删边困难加边容易。考虑先把没删掉的进行\(floyd\),然后反着操作加边。每次加边后\(floyd\)只需要\(n^2\)的时间复杂度来更新这条边的影响,也是\(floyd\)的典题了。具体就是假设加入的边是\(u, v\)边权是\(w\),那么枚举\(i, j\),用经过\(u, v\)这条边来更新答案:\(f[i][j] = \max(f[i][j], f[i][u] + f[u][v] + f[v][j], f[i][v] + f[v][u] + f[u][j])\)

点击查看代码
void solve() {
    int n, m, q;
    std::cin >> n >> m >> q;
    const i64 inf = 1e18;
    std::vector<std::array<int, 3>> edges(m);
    for (int i = 0; i < m; ++ i) {
    	int u, v, w;
    	std::cin >> u >> v >> w;
    	-- u, -- v;
    	edges[i] = {u, v, w};
    }

    std::vector<std::array<int, 3>> Q(q);
    std::vector<int> st(m);
    for (int i = 0; i < q; ++ i) {
    	int op;
    	std::cin >> op;
    	if (op == 1) {
    		int x;
    		std::cin >> x;
    		-- x;
    		Q[i][0] = op;
    		Q[i][1] = x;
    		st[x] = 1;
    	} else {
    		int x, y;
    		std::cin >> x >> y;
    		-- x, -- y;
    		Q[i][0] = op;
    		Q[i][1] = x;
    		Q[i][2] = y;
    	}
    }

    std::vector g(n, std::vector<i64>(n, inf));
    for (int i = 0; i < n; ++ i) {
    	g[i][i] = 0;
    }

    for (int i = 0; i < m; ++ i) {
    	if (!st[i]) {	
    		auto & [u, v, w] = edges[i];
    		g[u][v] = g[v][u] = std::min<i64>(g[u][v], w);
    	}
    }

    for (int k = 0; k < n; ++ k) {
    	for (int i = 0; i < n; ++ i) {
    		for (int j = 0; j < n; ++ j) {
    			g[i][j] = std::min(g[i][j], g[i][k] + g[k][j]);
    		}
    	}
    }

    std::reverse(Q.begin(), Q.end());

    std::vector<i64> ans;
    for (int i = 0; i < q; ++ i) {
    	auto & [op, x, y] = Q[i];
    	if (op == 1) {
    		auto & [u, v, w] = edges[x];
    		g[u][v] = g[v][u] = std::min<i64>(g[u][v], w);
    		for (int i = 0; i < n; ++ i) {
    			for (int j = 0; j < n; ++ j) {
    				g[i][j] = std::min(g[i][j], g[i][u] + g[u][v] + g[v][j]);
    				g[i][j] = std::min(g[i][j], g[i][v] + g[v][u] + g[u][j]);
    			}
    		}
    	} else {
    		ans.push_back(g[x][y] == inf ? -1 : g[x][y]);
    	}
    }

    std::reverse(ans.begin(), ans.end());

    for (auto & res : ans) {
    	std::cout << res << "\n";
    }
}

G - Road Blocked 2

题意:给你一个图,求有多少边使得它们被删去后最短路的距离增加。

首先这些边一定在某条最短路上,把这些边求出来,这是一个经典问题,只要两个点分别到起点和终点的距离加上边权等于最短路这条边就在最短路上。然后把这些边建一个新图,那么问题就是求这些边里哪些边删去后使得图不连通,\(trajan\)求割边模板。

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

    using PII = std::pair<i64, int>;
    auto dijkstra = [&](std::vector<i64> & dist, int s) -> void {
	    std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
	    dist[s] = 0;
	    heap.push({dist[s], s});
	    while (heap.size()) {
	    	auto [d, u] = heap.top(); heap.pop();
	    	if (d != dist[u]) {
	    		continue;
	    	}

	    	for (auto & [v, w] : adj[u]) {
	    		if (dist[v] > dist[u] + w) {
	    			dist[v] = dist[u] + w;
	    			heap.push({dist[v], v});
	    		}
	    	}
	    }
	};

    const i64 inf = 1e18;
    std::vector<i64> dists(n, inf), distt(n, inf);
    dijkstra(dists, 0);
    dijkstra(distt, n - 1);
    for (int i = 0; i < n; ++ i) {
    	adj[i].clear();
    }

    for (int i = 0; i < m; ++ i) {
    	auto & [u, v, w] = edges[i];
    	if (dists[u] + distt[v] + w == dists[n - 1] || dists[v] + distt[u] + w == dists[n - 1]) {
    		adj[u].push_back({v, i});
    		adj[v].push_back({u, i});
    		// std::cout << i << "\n";
    	}
    }

    std::vector<std::string> ans(m, "No");

    std::vector<int> dfn(n, -1), low(n);
    int idx = 0;
    auto dfs = [&](auto & self, int u, int fa) -> void {
    	dfn[u] = low[u] = idx ++ ;
    	for (auto & [v, i] : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		if (dfn[v] == -1) {
    			self(self, v, u);
    			low[u] = std::min(low[u], low[v]);
    			if (low[v] > dfn[u]) {
    				ans[i] = "Yes";
    			}
    		} else {
    			low[u] = std::min(low[u], dfn[v]);
    		}
    	}
    };

    dfs(dfs, 0, -1);
    for (int i = 0; i < m; ++ i) {
    	std::cout << ans[i] << "\n";
    }
}
posted @ 2025-03-12 21:15  maburb  阅读(59)  评论(0)    收藏  举报