結果
| 問題 |
No.2200 Weird Shortest Path
|
| ユーザー |
|
| 提出日時 | 2023-02-14 12:16:02 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 249 ms / 2,000 ms |
| コード長 | 6,299 bytes |
| コンパイル時間 | 2,578 ms |
| コンパイル使用メモリ | 212,848 KB |
| 最終ジャッジ日時 | 2025-02-10 15:17:38 |
|
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 44 |
コンパイルメッセージ
main.cpp:1:25: warning: extra tokens at end of #include directive
1 | #include <bits/stdc++.h>#pragma GCC optimize("Ofast")
| ^
main.cpp: In function ‘int main()’:
main.cpp:216:10: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
216 | loop(i, M)
| ^
main.cpp:12:38: note: in definition of macro ‘loop’
12 | #define loop(i, n) for (register int i = 0; i < n; i++)
| ^
main.cpp:226:10: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
226 | loop(i, M)
| ^
main.cpp:12:38: note: in definition of macro ‘loop’
12 | #define loop(i, n) for (register int i = 0; i < n; i++)
| ^
ソースコード
#include <bits/stdc++.h>#pragma GCC optimize("Ofast")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
using vect = vector<double>;
using Graph = vector<vector<ll>>;
#define endl '\n'
#define loop(i, n) for (register int i = 0; i < n; i++)
#define Loop(i, m, n) for (int i = m; i < n; i++)
#define pool(i, n) for (int i = n; i >= 0; i--)
#define Pool(i, m, n) for (int i = n; i >= m; i--)
#define modd 1000000007ll
// #define modd 998244353ll
#define flagcount(bit) __builtin_popcount(bit)
#define flag(x) (1ll << x)
#define flagadd(bit, x) bit |= flag(x)
#define flagpop(bit, x) bit &= ~flag(x)
#define flagon(bit, i) bit &flag(i)
#define flagoff(bit, i) !(bit & (1ll << i))
#define all(v) v.begin(), v.end()
#define low2way(v, x) lower_bound(all(v), x)
#define high2way(v, x) upper_bound(all(v), x)
#define idx_lower(v, x) (distance(v.begin(), low2way(v, x))) // 配列vでx未満の要素数を返す
#define idx_upper(v, x) (distance(v.begin(), high2way(v, x))) // 配列vでx以下の要素数を返す
#define idx_lower2(v, x) (v.size() - idx_lower(v, x)) // 配列vでx以上の要素数を返す
#define idx_upper2(v, x) (v.size() - idx_upper(v, x)) // 配列vでxより大きい要素の数を返す
#define idx_count(v, l, r) max(0, (int)(idx_upper(v, r) - idx_lower(v, l))) // 配列vでl<=x<=rを満たす要素xの数を返す
#define putout(a) cout << a << '\n'
#define Sum(v) accumulate(all(v), 0ll)
ll ctoi(char c)
{
if (c >= '0' && c <= '9')
{
return c - '0';
}
return -1;
}
template <typename T>
string make_string(T N)
{
string ret;
T now = N;
while (now > 0)
{
T x = now % 10;
ret += (char)('0' + x);
now /= 10;
}
reverse(all(ret));
return ret;
}
template <typename T>
T gcd(T a, T b)
{
if (a % b == 0)
{
return (b);
}
else
{
return (gcd(b, a % b));
}
}
template <typename T>
T lcm(T x, T y)
{
T z = gcd(x, y);
return x * y / z;
}
template <typename T>
bool primejudge(T n)
{
if (n < 2)
return false;
else if (n == 2)
return true;
else if (n % 2 == 0)
return false;
double sqrtn = sqrt(n);
for (T i = 3; i < sqrtn + 1; i++)
{
if (n % i == 0)
{
return false;
}
i++;
}
return true;
}
template <typename T>
bool chmax(T &a, const T &b)
{
if (a < b)
{
a = b; // aをbで更新
return true;
}
return false;
}
template <typename T>
bool chmin(T &a, const T &b)
{
if (a > b)
{
a = b; // aをbで更新
return true;
}
return false;
}
// 場合によって使い分ける
const ll dx[4] = {1, 0, -1, 0};
const ll dy[4] = {0, 1, 0, -1};
// const ll dx[8] = {1, 1, 0, -1, -1, -1, 0, 1};
// const ll dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
// cout << fixed << setprecision(10);
// vector<vector<ll>> field(h, vector<ll>(w));
template <typename T>
void putA(vector<T> &A)
{
int sz = A.size();
for (int i = 0; i < sz; i++)
{
cout << A[i];
if (i != sz - 1)
cout << " ";
else
cout << "" << endl;
}
}
ll extgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll d = extgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
struct union_find
{
vector<long long> par; // 親の番号
vector<long long> rank; // 木の深さ(根のランクは0)
vector<long long> siz; // 要素xが根なら木の頂点数を格納する
// 初期化子リストを用いた初期化
union_find(long long N) : par(N), rank(N), siz(N)
{
for (long long i = 0; i < N; i++)
{
par[i] = i;
rank[i] = 0;
siz[i] = 1;
}
}
// 要素xが所属する木の根を再帰的に発見する
long long root(long long x)
{
if (par[x] == x)
return x;
return par[x] = root(par[x]); // 経路圧縮
}
// 要素xが属する木と要素yが属する木の併合
void unite(long long x, long long y)
{
long long rx = root(x);
long long ry = root(y);
if (rx == ry)
return; // 同じ木に属してたらそのまま
if (rank[rx] < rank[ry])
{
par[rx] = ry; // 根がryの木に併合
siz[ry] = siz[rx] + siz[ry];
}
else
{
par[ry] = rx; // 根がrxの木に併合
siz[rx] = siz[rx] + siz[ry];
if (rank[rx] == rank[ry])
rank[rx]++;
}
}
// 要素xが属する木と要素yが属する木が同じならtrueを返す
bool same(long long x, long long y)
{
return root(x) == root(y);
}
// 要素xが属する木の頂点数を返す
long long size(long long x)
{
return siz[root(x)];
}
};
int main()
{
/*
どういう問題か
Σ{1<=u<v<=N} min{u,v間の全てのパス} max(u,v間のパスに含まれる辺の重み)
を求める。
f(u,v):min{u,v間の全てのパス} max(u,v間のパスに含まれる辺の重み)
とする。
辺を小さい順に構築していく。
すると、あるu,vについてu-v間が連結になった瞬間、そのときに結ばれた辺の重みw=f(u,v)となる。
後は、辺を追加するときに、どのu,v間を連結にしたかが分かればよい。これは併合する2つの集合のサイズの積に等しい。
解けた!
*/
ll N, M;
cin >> N >> M;
ll ans = 0;
vector<tuple<ll, ll, ll>> edges(M);
loop(i, M)
{
ll a, b, w;
cin >> a >> b >> w;
a--;
b--;
edges[i] = make_tuple(w, a, b);
}
sort(all(edges));
union_find tree(N);
loop(i, M)
{
ll w = get<0>(edges[i]), a = get<1>(edges[i]), b = get<2>(edges[i]);
if (tree.same(a, b))
continue;
ans += tree.size(a) * tree.size(b) * w;
tree.unite(a, b);
}
putout(ans);
return 0;
}