[CF1483D]Useful Edges

壹、题目描述 ¶

传送门 to Luogu.

贰、题解 ¶

\(\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

当匹配太乱时,固定一个参数吧,他会让你的枚举变得有序,重复枚举的部分也会变少。

posted @ 2021-05-15 10:19  Arextre  阅读(46)  评论(0编辑  收藏  举报