Scx117
只一眼,便辽阔了时间。

题意:给你一张n个点m条边的带权无向图。其中由s个点是加油站。询问从x加油站到y加油站,油箱容量<=b,能否走到?

n,m,q,s<=20W,b<=2e9。

 

标程:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<queue>
 4 #include<cstring>
 5 using namespace std;
 6 typedef long long ll;
 7 int read()
 8 {
 9   int x=0;char ch=getchar();
10   while (ch<'0'||ch>'9') ch=getchar();
11   while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
12   return x;
13 }
14 const int N=200005;
15 const int inf=0x3f3f3f3f;
16 struct node{int to,next,w;}num[N*2];
17 struct _node{int i;ll dis;_node(int A,ll B){i=A;dis=B;}};
18 struct Node{int u,v,id;ll dis;Node(){}; Node(int A,int B,ll C,int D){u=A;v=B;dis=C;id=D;}}a[N],e[N];
19 struct cmp{
20   bool operator () (const _node &A,const _node &B)
21   {return A.dis>B.dis;}
22 };
23 priority_queue<_node,vector<_node>,cmp>q;
24 int cnt,S,fa[N],u,v,w,n,s,m,head[N],vis[N],f[N],Q,ans[N],tot;
25 ll dis[N],b;
26 void add(int x,int y,int w)
27 {num[++cnt].to=y;num[cnt].next=head[x];num[cnt].w=w;head[x]=cnt;}
28 int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
29 bool operator < (const Node &A,const Node &B){return A.dis<B.dis;}
30 void dijk()
31 {
32   while (!q.empty())
33   {
34     _node now=q.top();q.pop();
35     if (vis[now.i]) continue;vis[now.i]=1;
36     for (int i=head[now.i];i;i=num[i].next)
37       if (dis[num[i].to]>dis[now.i]+num[i].w) 
38       {
39           dis[num[i].to]=dis[now.i]+num[i].w;q.push(_node(num[i].to,dis[num[i].to]));
40           fa[num[i].to]=fa[now.i];
41       }
42   }
43 }
44 void init()
45 { 
46    for (int i=1;i<=n;i++)
47      for (int j=head[i];j;j=num[j].next)
48        if (fa[i]!=fa[num[j].to]&&fa[i]<fa[num[j].to]) e[++tot]=Node(fa[i],fa[num[j].to],dis[i]+dis[num[j].to]+num[j].w,tot);
49 }
50 void mst()
51 {
52    int head=1;
53    for (int i=1;i<=n;i++) f[i]=i;
54    for (int i=1;i<=Q;i++)
55    {
56      while (head<=tot&&e[head].dis<=a[i].dis)
57      {
58         int x=e[head].u,y=e[head].v;
59         if (find(x)!=find(y)) f[find(x)]=find(y);
60         head++;
61      }
62      if (find(a[i].u)!=find(a[i].v)) ans[a[i].id]=0;else ans[a[i].id]=1;
63    }
64 }
65 int main()
66 {
67    n=read();s=read();m=read();
68    memset(dis,inf,sizeof(dis));
69    for (int i=1;i<=s;i++) S=read(),dis[S]=0,q.push(_node(S,0)),fa[S]=S;
70    for (int i=1;i<=m;i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
71    dijk();init();
72    Q=read();
73    for (int i=1;i<=Q;i++) a[i].u=read(),a[i].v=read(),a[i].dis=read(),a[i].id=i;
74    sort(a+1,a+Q+1);sort(e+1,e+tot+1);
75    mst();
76    for (int i=1;i<=Q;i++) puts(ans[i]?"TAK":"NIE");
77    return 0;
78 }

 

题解:并查集+技巧

要走到肯定是瞄准了加油站走。而且每次如果能走,往最近的加油站走答案一定不会变劣(你也可以走回来)。

那么就有一种高超的建图方法:

1.跑多源最短路,找到每个点离它最近的加油站,算出距离。

2.对于原图边(u,v),如果nearest[u]!=nearest[v],那么连边nearest[u]-nearest[v],边权为dist(u,nearest[u])+dist(v,nearest[v])+dist(u,v)。

这样离u,v最近的加油站就有经过(u,v)的一条路径了,但是不一定这样的dist(nearest[u],nearest[v])就是实际最短路,然而如果不是实际最短路,一定有另一个加油站k,离这边一个加油站更近,而且这样nearest[u]和nearest[v]一定通过另外几个加油站连通,实际走的策略也应该是经过另外几个加油站的。

离线掉询问按b从小到大排序,也按照b的顺序加入边,用并查集维护连通性。查询时,起点终点在同一个块中则可行。O((q+n)a)。

需要在线的话就构造出最小生成树,倍增/树剖找链上最大值。

posted on 2018-04-17 23:11  Scx117  阅读(182)  评论(0编辑  收藏  举报