BZOJ 3955 Surely You Congest 解题报告

首先,我们可以求出源为 $1$ 号点的最短路图以及各个点到 $1$ 号点的最短路。

然后我们考虑那些距离不同的点,是一定不会发生拥堵现象的。

然后我们就只需要考虑那些距离相同的点,就相当于做一个最大流嘛。

假设考虑与 $1$ 号节点距离为 $d$ 的点,那怎么连边,怎么设置源和汇呢?

  • 源为 $1$ 号节点,新开一个 $n+1$ 号节点作为汇。
  • 对于所有满足 $dist(1, x) + w(x,y) = dist(1, y)$ 的 $x,y$ 建一条 $x\rightarrow y$ 的边,容量为 $1$。
  • 如果某个点 $x$ 与 $1$ 号节点距离恰好为 $d$,建一条 $x\rightarrow T$ 的边,容量为这个点上车辆的数目。

然后把所有距离下的最大流加起来,就是答案了。

复杂度看起来有点高,不过加点优化应该还是能跑过去的。

我加了一个优化:如果与 $1$ 号节点距离为 $d$ 的车辆只有 $1$ 辆,那么最大流就是 $1$,就不用去跑网络流了。

感觉效果还不错。

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 using namespace std;
  7 typedef long long LL;
  8 #define C 2000 + 5
  9 #define N 50000 + 5
 10 #define M 400000 + 5
 11 #define SIZE 10000000 + 5
 12 #define INF 0x7fffffff
 13  
 14 int n, m, c, S, T, tot, _tot, ans;
 15 int A[C];
 16 int Head[N], _Head[N], Inq[N];
 17 int Dis[N], _Dis[N];
 18 int E[M][3];
 19 int q[SIZE];
 20  
 21 struct Edge
 22 {
 23     int next, node, flow, w;
 24 }h[M], _h[M];
 25  
 26 inline void addedge(int u, int v, int fl, int w)
 27 {
 28     h[++ tot].next = Head[u], Head[u] = tot;
 29     h[tot].node = v, h[tot].flow = fl, h[tot].w = w;
 30     h[++ tot].next = Head[v], Head[v] = tot;
 31     h[tot].node = u, h[tot].flow = 0, h[tot].w = w;
 32 }
 33  
 34 inline bool SPFA(int S)
 35 {
 36     for (int i = S; i <= T; i ++)
 37         Dis[i] = INF, Inq[i] = 0;
 38     int l = 1, r = 1;
 39     Dis[S] = 0, q[1] = S, Inq[S] = 1;
 40     while (l <= r)
 41     {
 42         int z = q[l ++];
 43         Inq[z] = 0;
 44         for (int i = Head[z]; i; i = h[i].next)
 45         {
 46             int d = h[i].node, p = h[i].flow, w = h[i].w;
 47             if (!p) continue ;
 48             if (Dis[d] > Dis[z] + w)
 49             {
 50                 Dis[d] = Dis[z] + w;
 51                 if (!Inq[d])
 52                 {
 53                     q[++ r] = d;
 54                     Inq[d] = r;
 55                 }
 56             }
 57             if (Inq[d] && Dis[d] < Dis[q[l]])
 58             {
 59                 int u = Inq[d], v = q[l];
 60                 q[l] = d, q[u] = v;
 61                 Inq[d] = l, Inq[v] = u;
 62             }
 63         }
 64     }
 65     return Dis[T] != INF;
 66 }
 67  
 68 inline void Copy()
 69 {
 70     _tot = tot;
 71     for (int i = S; i <= T; i ++)
 72         _Head[i] = Head[i], _Dis[i] = Dis[i];
 73     for (int i = 2; i <= tot; i ++)
 74         _h[i] = h[i];
 75 }
 76  
 77 inline void Restore()
 78 {
 79     tot = _tot;
 80     for (int i = S; i <= T; i ++)
 81         Head[i] = _Head[i];
 82     for (int i = 2; i <= _tot; i ++)
 83         h[i] = _h[i];
 84 }
 85  
 86 inline bool cmp(int u, int v)
 87 {
 88     return Dis[u] < Dis[v];
 89 }
 90  
 91 inline int dinic(int z, int inflow)
 92 {
 93     if (z == T || !inflow) return inflow;
 94     int ret = inflow, flow;
 95     for (int i = Head[z]; i; i = h[i].next)
 96     {
 97         int d = h[i].node, p = h[i].flow;
 98         if (Dis[d] != Dis[z] + 1) continue ;
 99         flow = dinic(d, min(ret, p));
100         ret -= flow;
101         h[i].flow -= flow, h[i ^ 1].flow += flow;
102         if (!ret) return inflow;
103     }
104     if (ret == inflow) Dis[z] = -1;
105     return inflow - ret;
106 }
107  
108 int main()
109 {
110     #ifndef ONLINE_JUDGE
111         freopen("3955.in", "r", stdin);
112         freopen("3955.out", "w", stdout);
113     #endif
114      
115     scanf("%d%d%d", &n, &m, &c);
116     S = 1, T = n + 1;
117     for (int i = 1; i <= m; i ++)
118     {
119         int u, v, w;
120         scanf("%d%d%d", &u ,&v, &w);
121         E[i][0] = u, E[i][1] = v, E[i][2] = w;
122         addedge(u, v, 1, w);
123         addedge(v, u, 1, w);
124     }
125     SPFA(1);
126     for (int i = 1; i <= c; i ++)
127         scanf("%d", A + i);
128     sort(A + 1, A + c + 1, cmp);
129     tot = 1;
130     memset(Head, 0, sizeof(Head));
131     for (int i = 1; i <= m; i ++)
132     {
133         if (Dis[E[i][0]] + E[i][2] == Dis[E[i][1]])
134             addedge(E[i][0], E[i][1], 1, 1);
135         if (Dis[E[i][1]] + E[i][2] == Dis[E[i][0]])
136             addedge(E[i][1], E[i][0], 1, 1);
137     }
138     Copy();
139     int l = 1, r;
140     for (; l <= c; l = r + 1)
141     {
142         for (r = l; r < c && _Dis[A[r + 1]] == _Dis[A[l]]; r ++) ;
143         if (r == l) ans ++;
144         else
145         {
146             Restore();
147             for (int i = l; i <= r; i ++)
148                 addedge(A[i], T, 1, 1);
149             while (SPFA(S))
150                 ans += dinic(S, INF);
151         }
152     }
153     printf("%d\n", ans);
154      
155     #ifndef ONLINE_JUDGE
156         fclose(stdin);
157         fclose(stdout);
158     #endif
159     return 0;
160 }
3955_Gromah

 

转载于:https://www.cnblogs.com/gromah/p/4427728.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值