結果
| 問題 |
No.114 遠い未来
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-07-03 23:03:55 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 2,932 ms / 5,000 ms |
| コード長 | 4,630 bytes |
| コンパイル時間 | 3,107 ms |
| コンパイル使用メモリ | 236,864 KB |
| 実行使用メモリ | 13,184 KB |
| 最終ジャッジ日時 | 2025-07-03 23:04:12 |
| 合計ジャッジ時間 | 14,789 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 25 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
pair<long long,vector<pair<int,int>>> SteinerTree(const vector<vector<pair<int,long long>>> &Graph,const vector<int> &X){
//頂点集合Xを含む全域木 O(N3^K+2^K(N+M)logN).
//return {cost,辺Ui-Vi集合(Ui<Vi)} 連結じゃないならinfを返す.
//重みが0以下があると多分だめ.
int N = Graph.size(),K = X.size(),K2 = 1<<K;
long long inf = 3e18;
vector<vector<long long>> dist(K2,vector<long long>(N,inf));
priority_queue<pair<long long,int>,vector<pair<long long,int>>,greater<>> Q;
for(int i=0; i<K; i++) dist.at(1<<i).at(X.at(i)) = 0;
for(int s=1; s<(K2); s++){
for(int t=(s-1)&s; t>0; t=(t-1)&s){
int p = t,q = s^t;
if(p < q) break;
for(int i=0; i<N; i++) dist.at(s).at(i) = min(dist.at(s).at(i),dist.at(p).at(i)+dist.at(q).at(i));
}
for(int i=0; i<N; i++) if(dist.at(s).at(i) != inf) Q.push({dist.at(s).at(i),i});
while(Q.size()){
auto [nowd,pos] = Q.top(); Q.pop();
if(dist.at(s).at(pos) != nowd) continue;
for(auto [to,w] : Graph.at(pos)){
if(nowd+w < dist.at(s).at(to)) dist.at(s).at(to) = nowd+w,Q.push({nowd+w,to});
}
}
}
long long cost = inf;
vector<pair<int,int>> edge;
{
int pos = min_element(dist.back().begin(),dist.back().end())-dist.back().begin(),S = K2-1;
cost = dist.at(S).at(pos);
if(cost == inf) return {inf,{}};
queue<pair<int,int>> SP; SP.push({S,pos});
while(SP.size()){
tie(S,pos) = SP.front(); SP.pop();
while(true){
bool cont = false;
for(auto [to,w] : Graph.at(pos)){
if(dist.at(S).at(to)+w == dist.at(S).at(pos)){
edge.push_back({min(to,pos),max(to,pos)});
cont = true; pos = to;
break;
}
}
if(cont) continue;
break;
}
if(dist.at(S).at(pos) > 0){
for(int T=(S-1)&S; T>0; T=(T-1)&S){
int p = T,q = S^T;
if(dist.at(p).at(pos)+dist.at(q).at(pos) == dist.at(S).at(pos)){
SP.push({p,pos}),SP.push({q,pos});
break;
}
}
}
}
}
return {cost,edge};
}
class UnionFind{
private:
vector<int> par,siz;
public:
UnionFind(int N){
par.resize(N,-1);
siz.resize(N,1);
}
int root(int x){ //連結成分の代表頂点を返す.
if(par.at(x) == -1) return x;
else return par.at(x) = root(par.at(x));
}
bool unite(int u, int v){ //u,vを連結する 連結してた->false,した->trueを返す.
u = root(u),v = root(v);
if(u == v) return false;
if(siz.at(u) < siz.at(v)) swap(u,v); //Union by size.
par.at(v) = u;
siz.at(u) += siz.at(v);
return true;
}
bool issame(int u, int v){ //同じ連結成分ならtrue.
if(root(u) == root(v)) return true;
else return false;
}
int size(int pos){return siz.at(root(pos));} //posの連結成分の大きさを返す.
};
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N,M,T; cin >> N >> M >> T;
vector<vector<pair<int,long long>>> Graph(N);
vector<tuple<int,int,int>> edge;
for(int i=0; i<M; i++){
int u,v; cin >> u >> v;
u--; v--;
int w; cin >> w;
edge.push_back({w,u,v});
Graph.at(u).push_back({v,w});
Graph.at(v).push_back({u,w});
}
vector<int> X(T);
for(auto &x : X) cin >> x,x--;
if(T <= 15) cout << SteinerTree(Graph,X).first << endl;
else{
vector<bool> isX(N);
for(auto x : X) isX.at(x) = true;
vector<int> Y;
for(int i=0; i<N; i++) if(isX.at(i) == false) Y.push_back(i);
int n = Y.size(),n2 = 1<<n,answer = 1001001001;
sort(edge.begin(),edge.end());
for(int s=0; s<n2; s++){
UnionFind Z(N);
int siz = T,now = 0;
for(int i=0; i<n; i++)if(s&(1<<i)) isX.at(Y.at(i)) = true,siz++;
for(auto [w,u,v] : edge){
if(isX.at(u) && isX.at(v) && Z.issame(u,v) == false) now += w,Z.unite(u,v);
}
if(Z.size(X.at(0)) == siz) answer = min(answer,now);
for(int i=0; i<n; i++)if(s&(1<<i)) isX.at(Y.at(i)) = false;
}
cout << answer << endl;
}
}