|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
总结与反思
G - 不好枚举, 可以考虑构造搜索答案, 要注意阶段的区分, 数字太大考虑同余 H - 求很多点之间的最短路我们可以建立超级源点向每个关键点连边, 图论相关一定要注意图是否连通, 构建新图考虑一个点到另外一个点的花费等等
F - MST Unification
简单题, 考试时候用 kruskal 重构树水过去了, 后来才发现大家边求 mst 边求答案...
考虑什么时候会发生 mst 不唯一的情况, 当然也就是两个边权值一样, 但是一个边加了另一个边加不了的时候, 所以没了#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
struct DSU{
int f[N];
DSU(){ iota(f, f + N, 0); }
int Find(int x){
if(f[x] == x) return x;
return (f[x] = Find(f[x]));
}
bool Iseq(int x, int y){
x = Find(x), y = Find(y);
return (x == y);
}
void Merge(int x, int y){
f[Find(x)] = Find(y);
}
} dsu;
struct Edge{
int u, v, w;
} e[N];
int n, m, ans;
int stk[N], top;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++){
int u, v, w;
cin >> u >> v >> w;
e[i] = {u, v, w};
}
sort(e + 1, e + m + 1, [](Edge& a, Edge& b){ return a.w < b.w; });
for(int i = 1; i <= m; i++){
int l = i, r = i;
while(r <= m && e[r].w == e[l].w) r++;
r--;
top = 0;
for(int j = l; j <= r; j++){
if(dsu.Iseq(e[j].u, e[j].v)) continue;
stk[++top] = j;
}
// 相同边权, 本来有可能被加进去的, 现在加不进去所以必须让她边权 +1
for(int j = 1; j <= top; j++){
if(dsu.Iseq(e[stk[j]].u, e[stk[j]].v)){
ans++;
continue;
}
dsu.Merge(e[stk[j]].u, e[stk[j]].v);
}
i = r;
}
cout << ans;
return 0;
}
G - [ABC077D] Small Multiple
没想出来, 后来发现很简单
枚举 d 会超时, 就想着构造答案
每次 K*d 这个数可以 *10 或者 +1, 后者会对数位和产生贡献, 然后广搜, 要求是 k 的倍数, 所以数字 %k, 最后 %k == 0 的状态就是最优解
解释一下为什么不用考虑进位:进位的情况一定对应一种其他的情况,我们发现另一种情况各位和更小,所以这种情况一定是不优的,所以存在f里的不存在进位的影响。#include <bits/stdc++.h>
using namespace std;
// K*d 不好枚举 d, 考虑构造答案
// 这时不仅要想到 dp 还要想到搜索(记忆化)
// 构造数字有 +1 *10 两个操作, 只需要判断 %K == 0 即可
// 代价分别是 1, 0, 用 deque 跑 0/1bfs 就行了
// 构造的阶段就是一位一位的枚举
const int N = 1e5 + 5;
struct St{
// %k 的余数, 现在的数位和
int num, sum;
};
int f[N];
deque<St> q;
int k;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> k;
memset(f, 0x3f, sizeof(f));
q.push_back({1, 1});
f[1] = 1;
int temp;
while(!q.empty()){
auto cur = q.front();
q.pop_front();
if(cur.num == 0){
cout << cur.sum;
return 0;
}
temp = cur.num*10 % k;
if(cur.sum < f[temp]){
f[temp] = cur.sum;
q.push_front({temp, cur.sum});
}
temp = (cur.num + 1) % k;
if(cur.sum + 1 < f[temp]){
f[temp] = cur.sum + 1;
q.push_back({temp, cur.sum + 1});
}
}
return 0;
}
G - Cheap Robot
没啥好说的, 没考虑搞超级源点, 写暴力也没判断是否连通, 挂了
我用了 kruskal 重构树, 其实求个最小生成树倍增求边上最大值一样的, 考虑两个点之间怎么走就出来了#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const int K = 2e5 + 5;
const int M = 3e5 + 5;
struct Node {
int id;
ll w;
bool operator<(const Node& t) const { return w > t.w; }
};
struct Kru {
int u, v;
ll w;
} edges[M << 1];
int cnt;
int n, m, k, q;
vector<Node> g[N];
vector<int> e[K];
ll dist[N], val[K];
bitset<N> vis;
priority_queue<Node> p;
int f[K][19], dep[K];
void Dijkstra() {
memset(dist, 0x3f, sizeof(dist));
p.push({ 0, 0 });
dist[0] = 0;
while (!p.empty()) {
auto u = p.top().id;
p.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto ed : g[u]) {
int v = ed.id;
ll w = ed.w;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
if (!vis[v])
p.push({ v, dist[v] });
}
}
}
}
struct DSU {
int f[K], tot;
void Init() {
iota(f, f + K, 0);
tot = n;
}
int Find(int x) {
if (x == f[x])
return x;
return (f[x] = Find(f[x]));
}
int Merge(int x, int y) {
x = Find(x), y = Find(y);
if (x == y)
return 0;
tot++;
f[x] = tot;
f[y] = tot;
e[tot].emplace_back(x);
e[tot].emplace_back(y);
e[x].emplace_back(tot);
e[y].emplace_back(tot);
return tot;
}
} dsu;
void Kruskal() {
dsu.Init();
for (int i = 1; i <= n; i++) {
for (auto j : g[i]) {
if (j.id > 0) {
edges[++cnt] = { i, j.id, dist[i] + dist[j.id] + j.w };
}
}
}
sort(edges + 1, edges + cnt + 1, [](Kru& a, Kru& b) { return a.w < b.w; });
int u, v, w, cur;
for (int i = 1; i <= cnt; i++) {
u = edges[i].u;
v = edges[i].v;
w = edges[i].w;
cur = dsu.Merge(u, v);
if (cur)
val[cur] = w;
}
}
void dfs(int u, int pa) {
f[u][0] = pa;
dep[u] = dep[pa] + 1;
for (int i = 1; i < 19; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (auto ed : e[u]) {
if (ed == pa)
continue;
dfs(ed, u);
}
}
int lca(int x, int y) {
if (dep[x] < dep[y])
swap(x, y);
int d = dep[x] - dep[y];
for (int i = 18; i >= 0; i--) {
if ((d >> i) & 1) {
x = f[x][i];
}
}
if (x == y)
return x;
for (int i = 18; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> k >> q;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({ v, w });
g[v].push_back({ u, w });
}
for (int i = 1; i <= k; i++) {
g[0].push_back({ i, 0 });
g[i].push_back({ 0, 0 });
}
Dijkstra();
Kruskal();
dfs(dsu.tot, 0);
while (q--) {
int x, y;
cin >> x >> y;
cout << val[lca(x, y)] << '\n';
}
return 0;
}
|
|