loj #2048. 「HNOI2016」最小公倍數

#2048. 「HNOI2016」最小公倍數

題目描述

給定一張 N NN 個頂點 M MM 條邊的無向圖(頂點編號爲 1,2,⋯,n 1,2, \cdots ,n1,2,,n),每條邊上帶有權值。全部權值均可以分解成 2a⋅3b 2^a \cdot 3^b2a​​3b​​ 的形式。html

如今有 q qq 個詢問,每次詢問給定四個參數 u uu、v vv、a aa 和 b bb,請你求出是否存在一條頂點 u uu 到 v vv 之間的路徑,使得路徑依次通過的邊上的權值的最小公倍數爲 2a⋅3b 2^a \cdot 3^b2a​​3b​​ 。node

注意:路徑能夠不是簡單路徑。ios

下面是一些可能有用的定義:
最小公倍數: k kk 個數 a1,a2,⋯,ak a_1 , a_2, \cdots , a_ka1​​,a2​​,,ak​​ 的最小公倍數是能被每一個 aia_iai​​ 整除的最小正整數。
路徑:路徑 P:P1,P2,,Pk 是頂點序列,知足對於任意 1≤i<k 1 \leq i < k1i<k ,節點 Pi P_iPi​​ 和 Pi+1 P_{i+1}Pi+1​​ 之間都有邊相連。
簡單路徑:若是路徑 P:P1,P2,,Pk 中,對於任意 1≤s≠t≤k 1 \leq s \neq t \leq k1stk 都有 Ps≠Pt P_s \neq P_tPs​​Pt​​ ,那麼稱路徑爲簡單路徑。
ide

輸入格式

輸入文件的第一行包含兩個整數 N NN 和 M MM ,分別表明圖的頂點數和邊數。
接下來 M MM 行,每行包含四個整數 u uu、v vv、a aa、b bb 表明一條頂點 u uu 和 v vv 之間、權值爲 2a⋅3b 2^a \cdot 3^b2a​​3b​​ 的邊。
接下來一行包含一個整數 q qq ,表明詢問數。
接下來 q qq 行,每行包含四個整數 u uu 、v vv 、a aa 和 b bb,表明一次詢問。
詢問內容請參見問題描述。
ui

輸出格式

對於每次詢問,若是存在知足條件的路徑,則輸出一行 Yes,不然輸出一行 Nospa

(注意:第一個字母大寫,其他字母小寫) 。code

樣例

樣例輸入

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

數據範圍與提示

對於全部的數據,1≤n,q≤50000, 1≤m≤100000, 0≤a,b≤1091 \leq n,q \leq 50000,\ 1 \leq m \leq 100000,\ 0 \leq a,b \leq 10^91n,q50000, 1m100000, 0a,b109​​。htm

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 50010
using namespace std;
int fa[maxn];
int n,m;
struct node{
    int u,v,a,b;
    bool operator < (const node &w) const {
        return a<w.a;
    }
}E[100010];
int find(int x){
    if(x==fa[x])return fa[x];
    return fa[x]=find(fa[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    int x,y,a,b;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].a,&E[i].b);
    int q;scanf("%d",&q);
    sort(E+1,E+m+1);
    for(int Case=1;Case<=q;Case++){
        for(int i=1;i<=n;i++)fa[i]=i;
        scanf("%d%d%d%d",&x,&y,&a,&b);
        if(x==y&&a==0&&b==0){puts("No");continue;}
        for(int i=1;i<=m;i++){
            if(E[i].a>a)break;
            if(E[i].b>b)continue;
            int f1=find(E[i].u),f2=find(E[i].v);
            if(f1!=f2)fa[f1]=f2;
        }
        if(find(x)!=find(y)){puts("No");continue;}
        int mxa=0,mxb=0;
        for(int i=1;i<=m;i++){
            if(E[i].a>a)break;
            if(E[i].b>b)continue;
            if(find(x)==find(E[i].v)&&find(x)==find(E[i].u)){
                mxa=max(mxa,E[i].a);
                mxb=max(mxb,E[i].b);
            }
        }
        if(mxa==a&&mxb==b)puts("Yes");
        else puts("No");
    }
    return 0;
}
20分 暴力
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100010
using namespace std;
int n,m,block,Q,top,opcnt;
int fa[maxn],mxa[maxn],mxb[maxn],ans[maxn],sz[maxn];
struct data{
    int x,y,a,b,id;
    void init(int i){
        id=i;
        scanf("%d%d%d%d",&x,&y,&a,&b);
    }
    bool operator < (const data &w)const{
        if(a==w.a)return b<w.b;
        return a<w.a;
    }
}e[maxn],q[maxn],st[maxn];
struct oper{int x,y,fa,mxa,mxb,sz;}op[maxn];
bool cmp(data x,data y){
    if(x.b==y.b)return x.a<y.a;
    return x.b<y.b;
}
int find(int x){
    if(x==fa[x])return fa[x];
    return find(fa[x]);
}
void goback() {
    for (int i=opcnt; i; i--) {
        int x=op[i].x,y=op[i].y;
        fa[x]=op[i].fa;
        mxa[y]=op[i].mxa;
        mxb[y]=op[i].mxb;
        sz[y]=op[i].sz;
    }
    opcnt=0;
}
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[++opcnt]=(oper){x,y,fa[x],mxa[y],mxb[y],sz[y]};
    if(x==y){
        mxa[y]=max(mxa[y],a),mxb[y]=max(mxb[y],b);
        return;
    }
    fa[x]=y,sz[y]+=sz[x];
    mxa[y]=max(mxa[x],max(mxa[y],a));
    mxb[y]=max(mxb[x],max(mxb[y],b));
}
int main(){
    scanf("%d%d",&n,&m);
    block=(int)sqrt(m);
    for(int i=1;i<=m;i++)e[i].init(i);
    sort(e+1,e+m+1);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)q[i].init(i);
    sort(q+1,q+Q+1,cmp);
    for(int i=1;i<=m;i+=block){
        top=0;
        for(int j=1;j<=Q;j++)
            if(q[j].a>=e[i].a && (i+block>m || q[j].a<e[i+block].a))
            st[++top]=q[j];
        sort(e+1,e+i+1,cmp);
        for(int j=1;j<=n;j++)fa[j]=j,mxa[j]=mxb[j]=-1,sz[j]=1;
        for(int j=1,k=1;j<=top;j++){
            while(k<i&&e[k].b<=st[j].b){
                merge(e[k].x,e[k].y,e[k].a,e[k].b);
                k++;
            }
            opcnt=0;
            for(int l=i;l<i+block && l<=m;l++)
                if(e[l].a<=st[j].a && e[l].b<=st[j].b)
                merge(e[l].x,e[l].y,e[l].a,e[l].b);
            int x=find(st[j].x),y=find(st[j].y);
            if(x==y && mxa[x]==st[j].a && mxb[x]==st[j].b)ans[st[j].id]=1;
            else ans[st[j].id]=0;
            goback();
        }
    }
    for(int i=1;i<=Q;i++){
        if(ans[i])puts("Yes");
        else puts("No");
    }
    return 0;
}
100分 並查集啓發式合併
相關文章
相關標籤/搜索