LG P3247 [HNOI2016]最小公倍数

Description

给定一张N个顶点M条边的无向图(顶点编号为1,2,...,n),每条边上带有权值。所有权值都可以分解成$2^a*3^b$的形式。

现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为$2^a*3^b$。

注意:路径可以不是简单路径。

下面是一些可能有用的定义,如果与其它地方定义不同,在本题中以下面的定义为准:

最小公倍数: $ k $ 个数 $ a_1 , a_2, \ldots, a_k $ 的最小公倍数是能被每个 $a_i$ 整除的最小正整数。

路径:顶点序列 $ P \colon P_1,P_2,\ldots,P_k $ 是一条路径,当且仅当 $k \geq 2$,且对于任意 $ 1 \leq i < k $ ,节点 $ P_i $ 和 $ P_{i+1} $ 之间都有边相连。

简单路径:如果路径 $ P \colon P_1,P_2,\ldots,P_k $ 中,对于任意 $ 1 \leq s \neq t \leq k $ 都有 $ P_s \neq P_t $ ,那么称 $P$ 为简单路径。

Solution

有用的边两种权值都不超过询问,所以对于每次询问只需要将权值小于等于询问的边加入即可

先对a权值排序,进行分块

将每个询问挂在块上保证之前的块的a都小于等于询问

每次处理一个块时,询问和边集对b排序,零散部分暴力加入

LG P3247 [HNOI2016]最小公倍数
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<stack>
#include<queue>
#include<cmath>
using namespace std;
int n,m,Q,B,siz,fa[50005],maxa[50005],maxb[50005],ans[50005];
bool vst[50005],vis[50005];
struct Node{
    int to,a,b;
};
struct Edge{
    int u,v,a,b,id;
}edge[100005],que[50005];
vector<Node>ve[50005];
vector<Edge>qu[1005];
stack<int>sta;
queue<int>q;
inline int read(){
    int w=0,f=1;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return w*f;
}
bool cmp1(Edge x,Edge y){return x.a<y.a;}
bool cmp2(Edge x,Edge y){return x.b<y.b;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++)edge[i]=(Edge){read(),read(),read(),read(),0};
    Q=read();
    for(int i=1;i<=Q;i++)que[i]=(Edge){read(),read(),read(),read(),i};
    sort(edge+1,edge+m+1,cmp1),sort(que+1,que+Q+1,cmp1),siz=1.5*m/sqrt(Q),B=(m+siz-1)/siz;
    int pos=1;
    for(int i=1;i<=Q;i++){
        while(que[i].a>=edge[min(pos*siz,m)].a&&pos<B)++pos;
        qu[pos].push_back(que[i]);
    }
    for(int i=1;i<=B;i++){
        int p=1;
        for(int j=1;j<=n;j++)fa[j]=j;
        memset(maxa,-1,sizeof(maxa)),memset(maxb,-1,sizeof(maxb)),sort(edge+1,edge+(i-1)*siz+1,cmp2),sort(qu[i].begin(),qu[i].end(),cmp2);
        for(int j=0;j<qu[i].size();j++){
            while(p<=(i-1)*siz&&qu[i][j].b>=edge[p].b){
                int fx=find(edge[p].u),fy=find(edge[p].v);
                if(fx>fy)swap(fx,fy);
                maxa[fx]=max(maxa[fx],max(maxa[fy],edge[p].a)),maxb[fx]=max(maxb[fx],max(maxb[fy],edge[p].b)),fa[fy]=fx,++p;
            }
            for(int k=(i-1)*siz+1;k<=i*siz&&k<=m;k++)if(edge[k].a<=qu[i][j].a&&edge[k].b<=qu[i][j].b){
                int u=find(edge[k].u),v=find(edge[k].v);
                if(!vst[u])vst[u]=true,sta.push(u);
                if(!vst[v])vst[v]=true,sta.push(v);
                ve[u].push_back((Node){v,edge[k].a,edge[k].b}),ve[v].push_back((Node){u,edge[k].a,edge[k].b});
            }
            int u=find(qu[i][j].u),v=find(qu[i][j].v),taga=-1,tagb=-1;
            if(!vst[u])vst[u]=true,sta.push(u);
            if(!vst[v])vst[v]=true,sta.push(v);
            q.push(u),vis[u]=true;
            while(q.size()){
                int u=q.front();
                q.pop(),taga=max(taga,maxa[u]),tagb=max(tagb,maxb[u]);
                for(int k=0;k<ve[u].size();k++){
                    taga=max(taga,ve[u][k].a),tagb=max(tagb,ve[u][k].b);
                    if(!vis[ve[u][k].to])vis[ve[u][k].to]=true,q.push(ve[u][k].to);
                }
            }
            ans[qu[i][j].id]=(vis[v]&&taga==qu[i][j].a&&tagb==qu[i][j].b);
            while(sta.size())ve[sta.top()].clear(),vst[sta.top()]=vis[sta.top()]=false,sta.pop();
        }
    }
    for(int i=1;i<=Q;i++)puts(ans[i]?"Yes":"No");
    return 0;
}
[HNOI2016]最小公倍数

 

上一篇:树链剖分学习笔记


下一篇:@atcoder - Japanese Student Championship 2019 Qualification - E@ Card Collector