NOIP2023模拟5联测26-u

noip十连测day4-零

完全无向图是指任意一对顶点间都有边连接的简单无向图, n n n 个结点的完全无向图有 M = n ( n − 1 ) 2 M=\dfrac{n(n-1)}{2} M=2n(n1) 条边。

如果一个 n n n 个结点的带权完全无向图, M M M 条边的权值分别是 [ 1 , M ] [1,M] [1,M] M M M 个整数(即任意两条边权值不同,任意一个权值仅属于一条边),则称这张图为一个暗物质图

现在给定一个 n n n 个结点 m m m 条边的带权无向连通图,边权是 [ 1 , M ] [1,M] [1,M] 中两两不等的整数,你需要添加 M − m M-m Mm 条边,使其成为一张暗物质图,且加边前后最小生成树的权值和不变。

问是否存在至少一种满足要求的加边方案。


在边权互不相等的情况下,最小生成树的权值和不变,意味着无论怎么加边,最小生成树的边始终不变。

证明:考虑 Kruskal 的过程,若改变了边,说明有更小的边加入其中,就会有一条原本的树边无法加入到树中,考虑这个状态,发现对于后面的加边无影响,而总的来看边权和减小了,矛盾。

有了这个结论,再考察 Kruskal 的过程,假设现在只有权值小于 v a l val val 的边加入树中,把整个图分成了 k k k 个连通块(初始有 n n n 个),第 i i i 个块的大小为 s i s_i si。未给定的权值小于 v a l val val 的边此时只能在连通块内连接,否则它就会加入最小生成树,非法。

考虑当前状态下能容纳多少条权值小于 v a l val val 的边。显然是每个块的 s i ( s i − 1 ) 2 \dfrac{s_i(s_i-1)}2 2si(si1) 减去块内已给定的边的数量之和。

我们不可能对于 1 ∼ M 1\sim M 1M 的每个权值进行判断,只需对给定的边权判断即可(可以看代码理解)。

在实现上,用并查集维护 s s s;求出块内给定边的个数,可以维护块内点连出外面的边,把它们存在块的“代表”,在合并时看一个块连向另一个块的边有多少,这里要用启发式合并保证时空复杂度;至于每个块的 s i ( s i − 1 ) 2 \dfrac{s_i(s_i-1)}{2} 2si(si1),只需在合并时,加上 s x ⋅ s y s_x\cdot s_y sxsy 即可。

时间复杂度 O ( n log ⁡ n + m log ⁡ m ) O(n\log n+m\log m) O(nlogn+mlogm)

#include
using namespace std;
typedef long long ll;
const ll INF=1e18;
const int N=5e5+1;
int n,m,fa[N],sz[N];
vector<int> v[N];
struct node
{
    int u,v;
    ll w;
    bool operator<(const node &a)const{
        return w<a.w;
    }
}edge[N];
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%lld",&edge[i].u,&edge[i].v,&edge[i].w);
        v[edge[i].u].push_back(edge[i].v);
        v[edge[i].v].push_back(edge[i].u);
    }
    int cnt=0;
    ll sum=0,sum1=0;
    sort(edge+1,edge+1+m);
    for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
    for(int i=1;i<=m;i++){
        if(sum-sum1<edge[i].w-i) puts("No"),exit(0);
        int x=find(edge[i].u),y=find(edge[i].v);
        if(x!=y){
            if(sz[x]>sz[y]) swap(x,y);
            sum+=1ll*sz[x]*sz[y];
            for(auto j:v[x]){
                if(find(j)==y) sum1++;
                else v[y].push_back(j);
            }
            v[x].clear();
            fa[x]=y;
            sz[y]+=sz[x];
            if(cnt==n-1) break;
        }
    }
    puts("Yes");
}

你可能感兴趣的:(算法)