宕掉了好几天。。。。。来发一个水题的题解
题意:给出一个有向图(n,m<=1e5),并给出一个询问集合,请求出集合中的点,两两之间的最短距离。
题解:回想最短路算法,首先排除掉N^3的的那个,然后剩下SPFA和Dijkstra跑多次的复杂度比较能接受,这两个其实是差不多的东西,由于边权都是正的,就上Dijkstra吧。
基础版的Dijkstra是单源多汇的,但是本题是多源多汇,但是Dij他是单源的……等等。。。Dij也可以多源呀,只要开一个超源0,用长度为0的边连接到各个起点,在把每个终点用长度为0的边连接到超汇n+1,这样0 – n+1的最短路就是从所有的起点到所有的终点路径中最短的。那么我们要想办法把真正最短答案的起点 分到起点集合中,把真正的最优终点放到终点集合,其他的随便放哪里都行。emmmm随机化算法随机分组。。。期望做4次可以得到正确答案。。。。
官解:按照点的标号的每个二进制位分组,最多分20次(准确的说是17次)。每次会把某一位不同的点分开到起点和终点集,然后再起点终点互换,再做一次。
正确性在于:对于任意两个点u和v,u和v是不同的点,必然有至少一个位不同,因此至少有一次他们被分到了各自正确的集合中,得到了正确答案,其他的答案都比他要大。
注意:这个题好像卡了vector的常数。。。模拟链表可以过掉。但是好像大家都是随机化算法搞得。。。
拓展:如果题目中没有环,还有另外一种哦做法:对于每个询问点x,连接0->x长度为0的边,对于每个点 i ,如果i有连接到某个询问点的边,那么把这条边重定向到 n+1点,从0到n+1跑一次就是答案。这个题的话。。。因为环路的存在。。。所以诸如1->2->3->1这样的道路也被统计到了。。。就得到了非法答案。。。。我也想不到什么好的方法。。。只好放弃了。。
PS:上面这个思路是我十分钟极限操作出来的。写的时候就担心有环。。最后果然WA了。
Code:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f3f3f3f3fLL
#define LL long long
#define p E[i].x
#define bit(x) (1<<(x))
using namespace std;
struct node
{
int x;
LL v;
bool operator<(const node &tmp)const
{
return v>tmp.v;
}
};
priority_queue<node> q;
struct edge
{
int x,to,v;
}E[N<<1];
int n,m,totE,g[N],X[N],Y[N],Z[N],a[N];
LL dist[N];
bool v[N];
void addedge(int x,int y,int v)
{
E[++totE] = (edge){y,g[x],v}; g[x] = totE;
}
LL calc_dist(int S,int T)
{
for(int i=0;i<=n+1;i++) dist[i] = INF, v[i] = 0;
dist[S]=0;
q.push((node){S,0});
node tmp;
while(!q.empty())
{
tmp = q.top(); q.pop();
int x = tmp.x;
if(v[x]) continue;
v[x] = 1;
for(int i=g[x];i;i=E[i].to)
if(!v[p] && dist[p]>dist[x]+E[i].v)
{
dist[p] = dist[x] + E[i].v;
q.push((node){p,dist[p]});
}
}
return dist[T];
}
int main()
{
//freopen("in0.txt","r",stdin);
int T,K,Te = 0;
cin>>T;
while(T--)
{
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=1;i<=K;i++) scanf("%d",&a[i]);
LL ans = INF;
for(int t=0;t<20;t++)
{
totE = 0;
for(int i=0;i<=n+1;i++) g[i] = 0;
for(int i=1;i<=m;i++) addedge(X[i],Y[i],Z[i]);
for(int i=1;i<=K;i++)
{
if(a[i]&bit(t)) addedge(0,a[i],0);
else addedge(a[i],n+1,0);
}
ans = min(ans, calc_dist(0,n+1));
totE = 0;
for(int i=0;i<=n+1;i++) g[i] = 0;
for(int i=1;i<=m;i++) addedge(X[i],Y[i],Z[i]);
for(int i=1;i<=K;i++)
{
if((a[i]&bit(t))==0) addedge(0,a[i],0);
else addedge(a[i],n+1,0);
}
ans = min(ans, calc_dist(0,n+1));
}
//assert( ans < INF );
printf("Case #%d: %lld\n",++Te,ans);
}
return 0;
}