牛牛的无向图【并查集】

题意

牛牛有一张 \(n\) 个点,\(m\) 条边的无向图,每条边有一个边权 \(w_i\)。我们定义一条路径的权值是这个路径包含的边的权值的最大值。定义 \(d(u,v)\) 表示在无向图中点 \(u\) 能到达点 \(v\) 的所有路径中权值最小的路径的权值。

现在牛牛给你 \(q\) 次询问,每次询问给出一个 \(L\) ,询问 \(\sum_{i=1}^{n}{\sum_{j=i+1}^{n}{[d(i,j)\leq L]}}\)。其中 \([C]\) 表示当命题 \(C\) 为真的时候为 \(1\) 否则为 \(0\)。比如 \([ \text{出题人很弱}]=1,[1 \geq 2] = 0\)

为了防止输入过大,选手需要在自己的程序内生成要输入的所有数据,可以参考如下代码:

unsigned int SA, SB, SC; int n, m, q, LIM;
unsigned int rng61(){
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}

void gen(){
    scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM);
    for(int i = 1; i <= m; i++){
        u[i] = rng61() % n + 1;
        v[i] = rng61() % n + 1;
        w[i] = rng61() % LIM;
    }
    for(int i = 1; i <= q; i++){
        L[i] = rng61() % LIM;
    }
}

链接:https://ac.nowcoder.com/acm/contest/7079/C

生成的 \(u_i,v_i,w_i\) 表示第 \(i\) 条边的属性,\(L_i\) 表示第 \(i\) 次询问输入的数。

分析

根据 \(d(u,v)\) 的定义,如果将边按照权值从小到大加入。对于不联通的两个点 \(u,v\) ,当加入一条边之后,两点变为联通。那么,该边的权值就是 \(d(u,v)\)。如果把询问的 \(L_i\) 和边权放在一起排序,那么,对于当前的 \(L_i\) ,其前面的可以将两个联通块相连的边都是满足的答案。联通块的合并可以借助并查集来实现。

代码

#include <bits/stdc++.h>

using namespace std;
const int N=1e6+5;
typedef long long ll;
unsigned int SA, SB, SC;
int n, m, q, LIM;
struct node
{
    int u,v,w,opt;
    bool operator < (const node b)const
    {
        if(w==b.w)
            return opt<b.opt;
        return w<b.w;
    }
}a[N];
int fa[N];
ll num[N];
unsigned int rng61(){
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}

void gen(){
    scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM);
    for(int i = 1; i <= m; i++){
        a[i].u = rng61() % n + 1;
        a[i].v = rng61() % n + 1;
        a[i].w = rng61() % LIM;
        a[i].opt=1;
    }
    for(int i = 1; i <= q; i++){
        a[i+m].w = rng61() % LIM;
        a[i+m].opt=2;
    }
}
int Find(int x)
{
    if(x!=fa[x])
        return fa[x]=Find(fa[x]);
    else
        return x;
}
int main()
{
    gen();
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        num[i]=1;
    }
    ll ans=0,res=0;
    sort(a+1,a+1+q+m);
    for(int i=1;i<=q+m;i++)
    {
        if(a[i].opt==1)
        {
            int x=Find(a[i].u);
            int y=Find(a[i].v);
            if(x!=y)
            {
                fa[x]=y;
                res+=1LL*num[x]*num[y];
                num[y]+=num[x];
            }
        }
        else ans^=res;
    }
    printf("%lld\n",ans);
    return 0;
}

posted @ 2020-08-30 11:13  xzx9  阅读(141)  评论(0编辑  收藏  举报