luogu P1710 地铁涨价

嘟嘟嘟

一道最短路好题。

首先明确一点,把一条边的边权变成2,等于删去这条边。因为变成2后最短路肯定不会经过这条边,就相当于删去这条边了。

所以题目变成了依次删去Q条边,求每一次删完边后有几个点的最短路变大了。

多做做题就会有这么个思维:删边不好办,然而逆向加边方便多了。所以30做法就是离线逆向加边,跑Q次dijkstra。时间复杂度(Qnlogn)。

正解是基于这个暴力的:想想dijkstra更新最短路的时候,如果成功从u更新了dis[v],那么前提一定是dis[u]已经是最短路。所以每一次我们可以不跑dijkstra,只用判断加的边(x, y)中,当前距离dis2[x]或是dis2[y]是否已经成为了最短路,如果是,就从这个点开始尝试更新他能走到的点的最短路(bfs, dfs都行,实测dfs更快)。然后如果到一个点的距离变成了最短路,ans--。

然后就是代码细节了:因为Q <= m,所以有一些边没删去,所以事先要加上,加边的时候不要每一次都判断dis2[x]或dis2[y]是否成为了最短路,而是在循环外面直接从1号节点更新,因为只有1号结点延伸出去的才可能成为最短路。而要是每一都判断dfs的话,虽然dfs会马上退出来,然而如果是一个菊花图的话,对于1号结点,每一次都遍历了很多条边,时间复杂度达到O(n2)。(这也就是我第7个点为啥一直TLE的原因……)

还有就是dis2实际上用一个bool数组就够,标记过就表示这个点已经成为了最短路,不用记录他的距离。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 1e5 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, m, q;
 38 struct Edge
 39 {
 40     int nxt, to;
 41 }e[maxn << 2];
 42 int head[maxn], ecnt;
 43 void add(int x, int y)
 44 {
 45     e[++ecnt] = (Edge){head[x], y};
 46     head[x] = ecnt;
 47 }
 48 
 49 struct Node
 50 {
 51     int x, y;
 52 }egs[maxn << 1];
 53 
 54 #define pr pair<int, int>
 55 #define mp make_pair
 56 bool in[maxn];
 57 int dis[maxn];
 58 void dijkstra(int s)
 59 {
 60     Mem(dis, 0x3f);
 61     dis[s] = 0;
 62     priority_queue<pr, vector<pr>, greater<pr> > q;
 63     q.push(mp(dis[s], s));
 64     while(!q.empty())
 65     {
 66         int now = q.top().second; q.pop();
 67         if(in[now]) continue;
 68         in[now] = 1;
 69         for(int i = head[now]; i != -1; i = e[i].nxt)
 70         {
 71             if(dis[e[i].to] > dis[now] + 1)
 72             {
 73                 dis[e[i].to] = dis[now] + 1;
 74                 q.push(mp(dis[e[i].to], e[i].to));
 75             }
 76         }
 77     }
 78 }
 79 
 80 bool vis[maxn << 1];
 81 int a[maxn << 1], ans[maxn << 1], num;
 82 
 83 void solve(int now, int d)
 84 {
 85     for(int i = head[now]; i != -1; i = e[i].nxt)
 86     {
 87         if(!vis[e[i].to] && d + 1 == dis[e[i].to])
 88         {
 89             num--;
 90             vis[e[i].to] = 1;
 91             solve(e[i].to, d + 1);
 92         }
 93     }
 94 }
 95 
 96 int main()
 97 {
 98     Mem(head, -1); ecnt = -1;
 99     n = read(); m = read(); q = read();
100     for(rg int i = 1; i <= m; ++i)
101     {
102         egs[i].x = read(); egs[i].y = read();
103         add(egs[i].x, egs[i].y); add(egs[i].y, egs[i].x);
104     }
105     dijkstra(1);
106     for(rg int i = 1; i <= q; ++i) a[i] = read(), vis[a[i]] = 1;
107     Mem(head, -1); ecnt = -1;
108     num = n - 1;
109     for(int i = 1; i <= m; ++i) if(!vis[i])
110     {
111         int x = egs[i].x, y = egs[i].y;
112         add(x, y); add(y, x);
113     }
114     Mem(vis, 0); vis[1] = 1;
115     solve(1, 0);
116     for(rg int i = q; i; --i)
117     {
118         ans[i] = num;
119         int x = egs[a[i]].x, y = egs[a[i]].y;
120         add(x, y); add(y, x);
121         if(vis[x]) solve(x, dis[x]);
122         if(vis[y]) solve(y, dis[y]);        
123     }
124     for(rg int i = 1; i <= q; ++i) write(ans[i]), enter;
125     return 0;
126 }
View Code

猜你喜欢

转载自www.cnblogs.com/mrclr/p/9847670.html