题意 :有向图,点集内的任意两点最短路径的最短值
思路 :很有意思的题目,直接两两枚举肯定不可以,这里考虑二进制分组,这样只需要分18次就能使得一次分组时最优解在两个不同的集合里面。
接下来就是分组后缩点一下跑他20次最短路。。很有意思!!
PS vector被卡了常数。。。
代码:
#include<bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define MEM(x,y) memset(x,y,sizeof(x));
#define bug(x) cout<<"bug"<<x<<endl;
typedef long long ll;
typedef pair<int,int> pii;
using namespace std;
const ll INF=1e18;
const int MAXN=200100;
struct qnode{
int v,c;
qnode(int _v=0,int _c=0):v(_v),c(_c) {}
bool operator <(const qnode &r)const{
return c>r.c;
}
};
struct Edge{
int to,next,val;
}edge[MAXN];
int head[MAXN],tot;
bool vis[MAXN];
ll dist[MAXN];
void init(){
tot = 0;memset(head,-1,sizeof(head));
}
void Dijkstra(int n,int start){
memset(vis,false,sizeof(vis));
for(int i=1; i<=n; i++)dist[i]=INF;
priority_queue<qnode>que;
while(!que.empty())que.pop();
dist[start]=0;
que.push(qnode(start,0));
qnode tmp;
while(!que.empty()){
tmp=que.top();
que.pop();
int u=tmp.v;
if(vis[u])continue;
vis[u]=true;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].to;
int cost=edge[i].val;
if(!vis[v]&&dist[v]>dist[u]+cost){
dist[v]=dist[u]+cost;
que.push(qnode(v,dist[v]));
}
}
}
}
void addedge(int u,int v,int w){
edge[tot].to = v;
edge[tot].val = w;
edge[tot].next = head[u];
head[u] = tot++;
}
bool st[MAXN];
int X[MAXN],Y[MAXN],Z[MAXN];
int main(){
int t,n,m,k,u,v,x,w;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++){
ll ans=INF;
MEM(st,0);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d%d",&X[i],&Y[i],&Z[i]);
scanf("%d",&k);
for(int i=0;i<k;i++) scanf("%d",&x),st[x]=1;
for(int ii=0;ii<=17;++ii){
int i=r[ii];
init();
for(int j=1;j<=m;++j){
u=X[j];v=Y[j];w=Z[j];
int two=1<<i;
if(st[u]&&(u&two)) u=n+1;
else if(st[u]&&!(u&two)) u=n+2;
if(st[v]&&(v&two)) v=n+1;
else if(st[v]&&!(v&two)) v=n+2;
if(u!=v)addedge(u,v,w);
}
Dijkstra(n+2,n+1);
ans=min(ans,dist[n+2]);
Dijkstra(n+2,n+2);
ans=min(ans,dist[n+1]);
}
printf("Case #%d: %lld
",ca,ans);
}
}