[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)

Posted HocRiser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)相关的知识,希望对你有一定的参考价值。

4537: [Hnoi2016]最小公倍数

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 1687  Solved: 607
[Submit][Status][Discuss]

Description

  给定一张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,那么称路径为简单路径。

Input

  输入文件的第一行包含两个整数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

Output

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

Sample Input

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

Sample Output

Yes
Yes
Yes
No
No

HINT

Source

[Submit][Status][Discuss]

首先如果只有一个参数a,可以直接将边和询问排序然后扫一遍即可。

现在是二维偏序问题,我们就需要合理分块了。

将边按a排序,询问按b排序,考虑分块,每次找到所有第一关键字在[L,R]中的询问,那么我们将第一关键字在[1,L)的边按第二关键字排序,就可以指针扫一遍统计答案了,对于块内的问题,直接暴力合并和撤销并查集操作即可。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const int N=100100;
 8 struct E{ int x,y,u,v,k; }a[N],b[N],c[N],h[N];
 9 void up(int &x,int y){ if (x<y) x=y; }
10 
11 bool ans[N];
12 int n,m,cnt,tot,fa[N],sz[N],fu[N],fv[N];
13 bool cmpu(const E &p,const E &q){ return p.u<q.u || (p.u==q.u && p.v<q.v); }
14 bool cmpv(const E &p,const E &q){ return p.v<q.v || (p.v==q.v && p.u<q.u); }
15 int getfa(int x){ return (x==fa[x]) ? x : getfa(fa[x]); }
16 
17 void merge(int x,int y,int u,int v){
18     x=getfa(x); y=getfa(y); if (sz[x]>sz[y]) swap(x,y);
19     h[++tot]=(E){x,y,fu[y],fv[y],sz[y]};
20     if (x!=y) fa[x]=y,sz[y]+=sz[x],up(fu[y],fu[x]),up(fv[y],fv[x]);
21     up(fu[y],u); up(fv[y],v);
22 }
23 
24 int main(){
25     freopen("bzoj4537.in","r",stdin);
26     freopen("bzoj4537.out","w",stdout);
27     scanf("%d%d",&n,&m);
28     rep(i,1,m) scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].u,&a[i].v);
29     sort(a+1,a+m+1,cmpu); scanf("%d",&cnt);
30     rep(i,1,cnt) scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].u,&b[i].v),b[i].k=i;
31     sort(b+1,b+cnt+1,cmpv);
32     int bl=sqrt(m);
33     for (int i=1; i<=m; i+=bl){
34         rep(j,1,n) fa[j]=j,fu[j]=fv[j]=-1,sz[j]=1;
35         int len=0;
36         rep(j,1,cnt) if (b[j].u>=a[i].u && (i+bl>m || b[j].u<a[i+bl].u)) c[++len]=b[j];
37         if (!len) continue;
38         if (i>1) sort(a+1,a+i,cmpv);
39         for (int j=1,k=1; j<=len; j++){
40             for (; k<i && a[k].v<=c[j].v; k++) merge(a[k].x,a[k].y,a[k].u,a[k].v);
41             tot=0;
42             for (int l=i; l<i+bl && l<=m; l++)
43                 if (a[l].u<=c[j].u && a[l].v<=c[j].v) merge(a[l].x,a[l].y,a[l].u,a[l].v);
44             int p=getfa(c[j].x),q=getfa(c[j].y);
45             ans[c[j].k]=(p==q && fu[p]==c[j].u && fv[p]==c[j].v);
46             for (; tot; tot--) p=h[tot].x,q=h[tot].y,fa[p]=p,fu[q]=h[tot].u,fv[q]=h[tot].v,sz[q]=h[tot].k;
47         }
48     }
49     rep(i,1,cnt) puts(ans[i]?"Yes":"No");
50     return 0;
51 }

 

以上是关于[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4537[Hnoi2016]最小公倍数 分块

bzoj4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数|分块

最小公倍数

[bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

BZOJ4540HNOI2016序列(莫队)