列表

详情


NC20118. [HNOI2016]最小公倍数

描述

给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b 的形式。
现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得 路径依次经过的边上的权值的最小公倍数为2^a*3^b。
注意:路径可以不是简单路径。
下面是一些可能有用的定义 :
最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数。
路径:路径P:P1,P2,…,Pk是顶点序列,满足对于任意1 ≤ i < k,节点Pi和Pi+1之间都有边相连。
简单路径:如果路径P:P1,P2,…,Pk中,对于任意1 ≤ s≠t ≤ k都有Ps≠Pt,那么称路径为简单路径。

输入描述

输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。
接下来M行,每行包含四个整数u、v、a、 b代表一条顶点u和v之间、权值为2^a*3^b的边。
接下来一行包含一个整数q,代表询问数。
接下来q行,每行包含四个整数u、v、a和b,代表一次询问。
询问内容请参见问题描述。1 ≤ n,q ≤ 50000、1 ≤ m ≤ 100000、0 ≤ a,b ≤ 10^9

输出描述

对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余字母小写) 。

示例1

输入:

4 5 
1 2 1 3 
1 3 1 2 
1 4 2 1 
2 4 3 2 
3 4 2 2 
5 
1 4 3 3 
4 2 2 3 
1 3 2 2 
2 3 2 2 
1 3 4 4

输出:

Yes
Yes
Yes
No
No

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 1364ms, 内存消耗: 5724K, 提交时间: 2019-10-29 22:09:34

// luogu-judger-enable-o2
//It is made by M_sea
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
using namespace std;

inline void chkmax(int& x,int y) { if (y>x) x=y; }
inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=200000+10;

struct Edge { int u,v,a,b; } e[N];
struct Query { int u,v,a,b,id; } q[N],o[N];
struct node { int u,v,a,b,sz; } sta[N];

inline int cmp1(Edge a,Edge b) { return a.a==b.a?a.b<b.b:a.a<b.a; }
inline int cmp2(Edge a,Edge b) { return a.b==b.b?a.a<b.a:a.b<b.b; }
inline int cmp3(Query a,Query b) { return a.b==b.b?a.a<b.a:a.b<b.b; }

int n,m,Q,top;
int ans[N];

struct UFS {
    int f[N],A[N],B[N],sz[N];
    inline void init(int n) {
        for (re int i=1;i<=n;++i) f[i]=i,A[i]=B[i]=-1,sz[i]=1;
    }
    inline int find(int x) { return x==f[x]?x:find(f[x]); }
    inline void merge(int x,int y,int a,int b) {
        x=find(x),y=find(y); if (sz[x]>sz[y]) swap(x,y);
        sta[++top]=(node){x,y,A[y],B[y],sz[y]};
        if (x!=y) f[x]=y,sz[y]+=sz[x],chkmax(A[y],A[x]),chkmax(B[y],B[x]);
        chkmax(A[y],a),chkmax(B[y],b);
    }
} S;

int main() {
    n=read(),m=read();
    for (re int i=1;i<=m;++i) e[i]=(Edge){read(),read(),read(),read()};
    Q=read();
    for (re int i=1;i<=Q;++i) q[i]=(Query){read(),read(),read(),read(),i};
    sort(e+1,e+m+1,cmp1); sort(q+1,q+Q+1,cmp3);
    for (re int i=1,sz=sqrt(m);i<=m;i+=sz) {
        S.init(n); int sum=0;
        for (re int j=1;j<=Q;++j)
            if (e[i].a<=q[j].a&&(q[j].a<e[i+sz].a||i+sz>m))
                o[++sum]=q[j];
        if (!sum) continue;
        if (i!=1) sort(e+1,e+i,cmp2);
        for (re int j=1,k=1;j<=sum;++j) {
            //第一类
            while (k<i&&e[k].b<=o[j].b) {
                S.merge(e[k].u,e[k].v,e[k].a,e[k].b);
                ++k;
            }
            //第二类
            top=0;
            for (re int l=i;l<i+sz&&l<=m;++l)
                if (e[l].a<=o[j].a&&e[l].b<=o[j].b)
                    S.merge(e[l].u,e[l].v,e[l].a,e[l].b);
            int u=S.find(o[j].u),v=S.find(o[j].v);
            if (u==v&&S.A[u]==o[j].a&&S.B[u]==o[j].b) ans[o[j].id]=1;
            else ans[o[j].id]=0;
            //撤销
            while (top) {
                int u=sta[top].u,v=sta[top].v; S.f[u]=u;
                S.A[v]=sta[top].a,S.B[v]=sta[top].b,S.sz[v]=sta[top].sz;
                --top;
            }
        }
    }
    for (re int i=1;i<=Q;++i) puts(ans[i]?"Yes":"No");
    return 0;
}

C++(clang++ 11.0.1) 解法, 执行用时: 1405ms, 内存消耗: 5852K, 提交时间: 2023-03-31 20:07:48

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

const int N=100010;
struct node{
    int x,y,a,b,id;
    friend bool operator <(const node &a,const node &b){
        if(a.a==b.a) return a.b<b.b;
        return a.a<b.a;
    }
    void init(int k){
        id=k; scanf("%d%d%d%d",&x,&y,&a,&b);
    }
}e[N],q[N],tmp[N];
struct opt{
    int x,y,f,da,db,sz;
}op[N];
int ans[N],f[N],da[N],db[N],sz[N],Q,n,m,top,tot,x,y,S;

bool cmp(const node &a,const node &b){
    if(a.b==b.b) return a.a<b.a;
    return a.b<b.b;
}
int find(int x){
    return f[x]==x?x:find(f[x]);
}
void merge(int x,int y,int a,int b){
    x=find(x); y=find(y);
    if(sz[x]>sz[y]) swap(x,y);
    op[++tot].x=x; op[tot].da=da[y]; op[tot].db=db[y];
    op[tot].y=y; op[tot].f=f[x]; op[tot].sz=sz[y];
    if(x==y){
        da[x]=max(da[x],a); 
        db[x]=max(db[x],b); 
        return;
    }
    f[x]=y; sz[y]+=sz[x];
    da[y]=max(da[y],max(da[x],a));
    db[y]=max(db[y],max(db[x],b));
}
void Return(){
    for(;tot;tot--){
        f[op[tot].x]=op[tot].f;
        db[op[tot].y]=op[tot].db;
        da[op[tot].y]=op[tot].da;
        sz[op[tot].y]=op[tot].sz;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) e[i].init(i);
    sort(e+1,e+1+m);
    S=(int)sqrt(m);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++) q[i].init(i);
    sort(q+1,q+1+Q,cmp);
    for(int i=1;i<=m;i+=S){
        top=0; 
        for(int j=1;j<=Q;j++)
        if(q[j].a>=e[i].a&&(i+S>m||q[j].a<e[i+S].a))
        tmp[++top]=q[j];
        sort(e+1,e+i,cmp);
        for(int j=1;j<=n;j++) f[j]=j,sz[j]=1,da[j]=db[j]=-1;
        for(int j=1,k=1;j<=top;j++){
            for(;k<i&&e[k].b<=tmp[j].b;k++)
            merge(e[k].x,e[k].y,e[k].a,e[k].b);
            tot=0;
            for(int l=i;l<i+S&&l<=m;l++)
            if(e[l].a<=tmp[j].a&&e[l].b<=tmp[j].b)
            merge(e[l].x,e[l].y,e[l].a,e[l].b);
            x=find(tmp[j].x); y=find(tmp[j].y);
            ans[tmp[j].id]=x==y&&da[x]==tmp[j].a&&db[x]==tmp[j].b;
            Return();
        }
    }
    for(int i=1;i<=Q;i++)
    if(ans[i]) puts("Yes");
    else puts("No");
    return 0;
}

上一题