• bzoj4144 Petrol


    题意:给你一张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)。

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

  • 相关阅读:
    C++的常量折叠(一)
    如何写面向互联网公司的求职简历
    所有的程序员都是自学成才
    [一个经典的多线程同步问题]解决方案一:关键段CS
    [一个经典的多线程同步问题]问题引入
    多线程笔记--原子操作Interlocked系列函数
    【分治法】归并分类
    内存字节对齐一网打尽,再也不纠结
    在C语言中基本数据类型所占的字节数
    多线程笔记--先了解工具
  • 原文地址:https://www.cnblogs.com/Scx117/p/8863554.html
Copyright © 2020-2023  润新知