[CF1483D]Useful Edges
壹、题目描述 ¶
贰、题解 ¶
记 \(\text{dis}(a,b)\) 表示图中由 \(a\) 到 \(b\) 的最短距离。
考虑一对三元组 \(\lang u_i,v_i,l_i\rang\),对于一条边 \(\lang a,b,w\rang\),如果它是 “有用的”,那么必须满足:
\[\text{dis}(u_i,a)+w+\text{dis}(b,v_i)\le l_i
\]
显然,我们暴力去匹配一个三元组和一条边,复杂度达到了 \(\mathcal O(n^4)\),这是不可接受的。
考虑枚举的重复点,似乎对于每一条边我们都重复访问了许多次,我们能否固定一个三元组的其中一个点,用这个点去匹配边?如果我们将三元组的 \(u_i\) 固定,即我们选择枚举 \(u\),那么我们的不等式变成了:
\[\begin{aligned}
&\text{dis}(u,a)+w+\text{dis}(b,v_i)\le l_i \\
\Rightarrow &\text{dis}(u,a)+w\le l_i-\text{dis}(b,v_i)
\end{aligned}
\]
括号左边,就是我们枚举时的固定值,对于右边,我们发现它实际上和独立的 \(b\) 有关,我们能否将 \(l_i-\text{dis}(b,v_i)\) 预处理出来?
若
\[\text{cei}(u,b)\overset\Delta=\min\{l-\text{dis}(b,v)\mid \lang u,v,l\rang\in T\}
\]
其中 \(T\) 是三元组集合。
那么,我们的不等式就是
\[\text{dis}(u,a)+w\le \text{cei}(u,b)
\]
考虑 \(\text{cei}(u,b)\) 如何快速处理?只需要枚举每一个三元组,再枚举一个点即可。
预处理复杂度 \(\mathcal O(n^3)\),跑 \(\tt floyd\) 的复杂度是 \(\mathcal O(n^3)\),计算答案的复杂度是 \(\mathcal O(n^3)\),那么总复杂度就是 \(\mathcal O(n^3)\) 了。
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define NDEBUG
#include<cassert>
#define Endl putchar('\n')
#define mp(a, b) make_pair(a, b)
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define fep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
typedef long long ll;
typedef pair<int, int> pii;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
const int maxn=600;
const int maxm=maxn*maxn;
struct edge{
int u, v, w;
edge(){}
edge(int U, int V, int W): u(U), v(V), w(W){}
}e[maxm+5];
struct trituple{
int u, v, l;
trituple(){}
trituple(int U, int V, int L): u(U), v(V), l(L){}
}tri[maxm+5];
ll f[maxn+5][maxn+5];
int n, m, q;
inline void input(){
n=readin(1), m=readin(1);
memset(f, 0x3f, sizeof f);
int u, v, w;
rep(i, 1, m){
u=readin(1), v=readin(1), w=readin(1);
f[u][v]=f[v][u]=w;
e[i]=edge(u, v, w);
}
}
inline void floyd(){
rep(i, 1, n) f[i][i]=0;
rep(k, 1, n) rep(i, 1, n) if(i!=k)
rep(j, 1, n) if(j!=i && j!=k)
f[i][j]=min(f[i][j], f[i][k]+f[k][j]);
}
inline void gettuple(){
q=readin(1);
int u, v, l;
rep(i, 1, q){
u=readin(1), v=readin(1), l=readin(1);
tri[i]=trituple(u, v, l);
}
}
ll cei[maxn+5][maxn+5];
inline void getminn(){
memset(cei, -0x3f, sizeof cei);
int u, v, l;
rep(i, 1, q){
u=tri[i].u, v=tri[i].v, l=tri[i].l;
rep(i, 1, n){
cei[u][i]=max(l-f[i][v], cei[u][i]);
cei[v][i]=max(l-f[i][u], cei[v][i]);
}
}
}
int vis[maxm+5];
inline void getans(){
int ans=0, a, b, w;
rep(u, 1, n){
rep(i, 1, m) if(!vis[i]){
a=e[i].u, b=e[i].v, w=e[i].w;
if(f[u][a]+w<=cei[u][b] || f[u][b]+w<=cei[u][a])
vis[i]=1, ++ans;
}
}
printf("%d\n", ans);
}
signed main(){
input();
floyd();
gettuple();
getminn();
getans();
return 0;
}
肆、用到 の Trick
当匹配太乱时,固定一个参数吧,他会让你的枚举变得有序,重复枚举的部分也会变少。