例题LuoguP2685 桥
题意就是给一张存在重边和自环的图,求出使得删去一条边后最短路长度最长的方案数。(当然不能删掉割边,不然直接嗝屁了)
分析
首先考虑删哪条边,和最短路相关的边就只有在最短路上和不在最短路上的边。
如果删掉的边不在最短路上,那么最短路长度不会变大,所以要删的边只能是最短路上的边。
然后我们需要知道一个性质,
我们可以在脑子里想出一个简单的图,1和n是两个端点,我们要在这个最短路上删去一条边,那么删去后的最短路一定满足只有一段不是原最短路上的;
简单的证明:如果有两段不是原最短路上的,直接把其中一段改成最短路上的显然更优。
接下来的思考有两个方向,一个是枚举最短路上的边删除,再跑最短路,复杂度必然是轰轰烈烈的\({O(L*nlogn)}\),随便爆炸的。
另一个方向是枚举所有非最短路上的边,如果删除某条边后的最短路经过这条边,最短路长度是多少。简单这样思考必然还是复杂度大爆炸的;
考虑到删除最短路上某一条边时,新的最短路一定是只和部分非原最短路上的边相关的,那么反过来思考也就是说部分非原最短路上的边一定是会影响到新最短路的。
这样子考虑,枚举非原最短路上的边,假设新的最短路经过这条边,那么一定会在原最短路上有一段连续的边满足:删去这条边后的新最短路一定经过枚举的非原最短路的边。
现在要找到这样一段连续的边,我们以其中一个端点为源点bfs,从枚举的边的靠近源点的这个点出发,不断向父节点走直到走到最短路上,此时最短路上的这个点就是我们需要找到的这一段的一个端点,另一边同理即可。
也就是说对于每条非原最短路上的边,可以对最短路上一个一段连续的边的答案产生影响,题中所求为最短路,所以可以用线段树维护区间最小值,最后统计整个区间的最大值即可。
因为线段树只需要维护最小值,所以线段树区间修改的时候只需要简单的打上标记即可,最后一次性下传标记。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL),cout.tie(NULL)
const int N = 1e3 + 5, M = 1e6 + 5, inf = 0x3f3f3f3f;
struct Edge{
int to, next, val;
}e[M << 1];
int head[N], tot;
inline void add(int u, int v, int w){
e[++tot].to = v, e[tot].next = head[u], e[tot].val = w;
head[u] = tot;
}
int n, m, cnt;
int dis1[N], dis2[N], sway[N], id[N], lm[N], rm[N], ans[N];
bool vis[N], in[M << 1];
struct SegmentTree{
#define ls(i) i << 1
#define rs(i) (i << 1) + 1
int minn[N << 2];
void build(int i, int l, int r){
minn[i] = inf;
if (l == r) return;
int mid = l + (r - l >> 1);
build(ls(i), l, mid);
build(rs(i), mid + 1, r);
}
void modify(int i, int l, int r, int o, int s, int c){
if (o <= l && r <= s){
minn[i] = min(minn[i], c);
return;
}
int mid = l + (r - l >> 1);
if (mid >= o) modify(ls(i), l, mid, o, s, c);
if (mid < s) modify(rs(i), mid + 1, r, o, s, c);
}
void pushdown(int i, int l, int r){
if (l == r){
ans[l] = minn[i];
return;
}
minn[ls(i)] = min(minn[ls(i)], minn[i]), minn[rs(i)] = min( minn[rs(i)], minn[i] );
int mid = l + (r - l >> 1);
pushdown(ls(i), l, mid);
pushdown(rs(i), mid + 1, r);
}
}T;
void dijkstra(int *dis, int s){
for (int i(1); i <= n; ++i) dis[i] = inf, vis[i] = false;
dis[s] = 0;
priority_queue<pair<int, int> > q;
q.push({0, s});
while (!q.empty()){
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = true;
for (int i(head[u]); i; i = e[i].next){
int v = e[i].to;
if (vis[v]) continue;
if (dis[u] + e[i].val < dis[v]){
dis[v] = dis[u] + e[i].val;
q.push({-dis[v], v});
}
}
}
}
void getlink(){
int u = 1;
while (u != n){
sway[++cnt] = u;
id[u] = cnt;
for (int i(head[u]); i; i = e[i].next){
int v = e[i].to;
if (dis2[u] == dis2[v] + e[i].val){
in[i] = true;
u = v;
break;
}
}
}
sway[++cnt] = n;
id[n] = cnt;
}
void bfs(int u, int *dis, int *a){
queue<int> q;
q.push(sway[u]);
a[sway[u]] = u;
while(!q.empty()){
int x = q.front(); q.pop();
for (int i(head[x]); i; i = e[i].next){
int v = e[i].to;
if (!id[v] && !a[v] && dis[x] + e[i].val == dis[v]){
a[v] = u;
q.push(v);
}
}
}
}
void doit(){
cin >> n >> m;
for (int i(1), x, y, z; i <= m; ++i){
cin >> x >> y >> z;
add(x, y, z), add(y, x, z);
}
dijkstra(dis2, n);
dijkstra(dis1, 1);
getlink();
for (int i(1); i <= cnt; ++i) bfs(i, dis1, lm);
for (int i(cnt); i >= 1; --i) bfs(i, dis2, rm);
T.build(1, 1, cnt);
for (int u(1); u <= n; ++u)
for (int i(head[u]); i; i = e[i].next){
if (in[i]) continue;
int v = e[i].to;
if (lm[u] > 0 && rm[v] > 0 && lm[u] < rm[v]) T.modify(1, 1, cnt, lm[u], rm[v] - 1, dis1[u] + e[i].val + dis2[v]);
}
T.pushdown(1, 1, cnt);
int fans = 0, fcnt = 0;
for (int i(1); i < cnt; ++i){
if (ans[i] > fans){
fans = ans[i];
fcnt = 1;
}
else if (ans[i] == fans) ++fcnt;
}
if (fans == dis1[n]) fcnt = m;
cout << fans << " " << fcnt;
// for (int i(1); i < cnt; ++i){
// if (ans[i] > fans)
// fans = ans[i];
// }
// cout << fans;
}
int main(){
IOS;
//freopen("in.txt", "r", stdin);
int T = 1;
while (T--){
doit();
}
return 0;
}
标签:cnt,路上,int,短路,mid,思路,删边,dis
From: https://www.cnblogs.com/ancer/p/17992959