嘘~ 正在从服务器偷取页面 . . .

模拟赛题讲解[1]


模拟赛题讲解[1]

来自 Roundgod 2022-07-25 noi.ac #2676

题目描述

给定一个带权连通无向图 \(G=(V,E)\) ,其中顶点个数 \(|V|=n\) ,边数\(|E|=m\) 图中可能包含重边以及自环。顶点编号为 \(1−n\) ,边的编号为 \(1−m\) ,第 \(i\) 条边连接顶点 \(a_i\)\(b_i\),权值为 \(c_i\). 输入保证图中所有边的权值各不相同,即对于所有\(1\le i<j \le n\) ,都有 \(c_i\ne c_j\)

你现在需要处理 \(q\) 个询问,其中第 \(k\) 个询问是一个三元组 \((u_i,v_i,w_i)\) ,表示:

如果在图 \(G\) 中添加一条连接 \(u_i\)\(v_i\) 权值为 \(w_i\) 的边得到图 \(G^{\prime}\) ,图 \(G^{\prime}\) 的最小生成树是否一定包含新添加的边?

询问保证对于所有 \(1 \le j \le m\) ,都有 \(w_i\ne c_j\)

输入格式

输入第一行包含 \(3\) 个整数 \(n,m,q\) ,分别表示图的顶点数,图的边数,以及询问的个数。

接下来 \(m\) 行,每行包含三个整数 \(a_i,b_i,c_i(1\le a_i,b_i\le n)\) ,表示第 \(i\) 条边连接的顶点以及权值。

接下来 \(q\) 行,每行 \(u_i,v_i,w_i(1\le u_i,v_i \le n)\) , 表示第 \(i\) 组询问。

输出格式

对于每组询问,根据答案你需要在一行中输出 Yes 或者 No

输入1

5 6 3
1 2 2
2 3 3
1 3 6
2 4 5
4 5 9
3 5 8
1 3 1
3 4 7
3 5 7

输出1

Yes
No
Yes

数据范围

对于 \(50\%\) 的数据,\(1\le n \le 200,~1\le m \le 1000,~1\le q\le 1000,~1\le c_i,w_i \le 10^9\)

对于 \(100\%\)的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5,~1\le q\le 2\times 10^5,~1\le c_i,w_i\le 10^{18}\)

题解

这个题还是蛮巧妙的

首先考虑 \(q=1\) 的情况,

显然此时我们可以把这条边直接加进去跑一遍 \(\text{kruskal}\)

然后遇到这条边的时候,判断能否加入最小生成树

然后我们考虑 \(q>1\) 的情况

最简单的想法是对于每个询问都跑一次,显然不可接受

仔细思考一下,就会发现,其实我们在判断的时候,

还是在原生成树的基础上尝试加入这条边的

而这个生成树是会把边一条一条从小到大加进去的

所以如果 \(w_i\) 比较小,那么它会被先询问到

考虑离线处理所有询问,直接加入生成树去跑,然后只连原图的边

时间复杂度 \(O((m+q) \log (m+q))\)

数组别忘了开两倍哦~

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <set>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N (int)(5e5+15)

int n,m,Q,pos,f[N],r[N],ans[N];
set<int> s;
struct Edge{int u,v,w,id;} e[N];
bool operator<(Edge a,Edge b){return a.w<b.w;}
void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int x,int y)
{
    x=find(x); y=find(y);
    if(x==y) return;
    if(r[x]<r[y]) f[x]=y;
    else f[y]=x,r[x]+=r[x]==r[y];
}
void kruskal()
{
    sort(e+1,e+1+pos); init(n);
    for(int i=1; i<=pos; i++)
    {
        if(e[i].id==-1)
        {
            if(find(e[i].u)!=find(e[i].v))
                merge(e[i].u,e[i].v);
        }else
        {
            if(find(e[i].u)!=find(e[i].v))
                ans[e[i].id]=1;
            else ans[e[i].id]=0;
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n); read(m); read(Q);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u); read(v); read(w);
        s.insert(w); e[++pos]={u,v,w,-1};
    }
    for(int i=1,u,v,w; i<=Q; i++)
    {
        read(u); read(v); read(w);
        s.insert(w); e[++pos]={u,v,w,i};
    }
    kruskal();
    for(int i=1; i<=Q; i++)
        if(ans[i]) puts("Yes");
        else puts("No");
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录