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排序,零散部分暴力加入
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }