[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 }

原文地址:https://www.cnblogs.com/HocRiser/p/8727490.html

时间: 2024-08-29 17:12:33

[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)的相关文章

Codeforces 506D Mr. Kitayuta&#39;s Colorful Graph(分块 + 并查集)

题目链接  Mr. Kitayuta's Colorful Graph 把每种颜色分开来考虑. 所有的颜色分为两种:涉及的点的个数 $> \sqrt{n}$    涉及的点的个数 $<= \sqrt{n}$ 对于第一种颜色,并查集缩点之后对每个询问依次处理过来若两点连通则答案加一. 对于第二种颜色,并查集所点之后对该颜色涉及的所有点两两之间判断是否连通, 若连通则另外开一个map记录答案. 最后把两个部分的答案加起来即可. 细节问题  由于每种颜色处理完之后并查集都要重新初始化,对于第一种颜色

P5443 [APIO2019]桥梁 [分块+并查集]

分块+并查集,大板子,没了. 并查集不路径压缩,可撤销,然后暴力删除 这样对于每个块都是独立的,所以直接搞就行了. 然后块内修改操作搞掉,就是单独的了 // powered by c++11 // by Isaunoya #include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); ++i) #define Rep(i, x, y) for (register int i = (x);

【BZOJ4537】[Hnoi2016]最小公倍数 分块

[BZOJ4537][Hnoi2016]最小公倍数 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:P

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

暴力的做法就是直接找到所有a,b都小于等于某个询问的边然后并查集合并,维护每个集合的a,b得最大值看是否等于询问的a,b 然后就可以考虑分块,把边按照a排序,每隔n?√分为一块 块前的按照b值排序按顺序插入,块内的暴力判断,并查集合并,每次都把块内合并的记录下来,处理完某个询问时就撤回并查集的操作 块的大小为n?√可能会T 改成n?log2n????????√可能会快一点 #include<algorithm> #include<iostream> #include<cstd

HDU 6271 Master of Connected Component(2017 CCPC 杭州 H题,树分块 + 并查集的撤销)

题目链接  2017 CCPC Hangzhou Problem H 思路:对树进行分块.把第一棵树分成$\sqrt{n}$块,第二棵树也分成$\sqrt{n}$块.    分块的时候满足每个块是一个连通块,那么每个块就有一个共同的祖先. 把询问按照第一个点被第一棵树的哪个祖先管辖和第二个点被第二棵树的哪个祖先管辖,分成$n$类. 每一类询问一起处理,处理完后用可撤销并查集恢复到之前的状态. 每一类询问之间依次转移,每次转移,移动次数不会超过$\sqrt{n}$次. 最后总时间复杂度$O(n^{

BZOJ4537 : [Hnoi2016]最小公倍数

将边按$a$从小到大排序,每$\sqrt{m}$个取一个关键点. 对于每个关键点,将这个点之前的边以及要在这个关键点回答的询问按$b$排序. 依次加入这个关键点之前的每条边,用并查集维护每个连通块$a$和$b$的最大值. 对于零碎部分,只有$\sqrt{m}$条边,暴力加入即可. 用一个栈按时间记录所有修改操作,然后撤销操作即可. 时间复杂度$O(m\sqrt{m}\log n)$. #include<cstdio> #include<algorithm> using namesp

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 400[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公

普林斯顿公开课 算法1-10:并查集-优化的快速合并方法

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法1-11:并查集的应用

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图