4537: [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: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

题解:

http://blog.csdn.net/thy_asdf/article/details/51203421

注意并查集不能路径压缩,要用启发式合并

code:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 using namespace std;
 7 char ch;
 8 bool ok;
 9 void read(int &x){
10     for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch==‘-‘) ok=1;
11     for (x=0;isdigit(ch);x=x*10+ch-‘0‘,ch=getchar());
12     if (ok) x=-x;
13 }
14 const int maxn=150005;
15 int n,m,q,rm,tot,cnt,fa[maxn],siz[maxn],maxa[maxn],maxb[maxn];
16 int find(int x){return x==fa[x]?x:find(fa[x]);}
17 bool ans[maxn];
18 struct Data{
19     int u,v,a,b,id;
20     void init(int i){read(u),read(v),read(a),read(b),id=i;}
21 }edge[maxn],quer[maxn],tmp[maxn];
22 bool cmpa(const Data &x,const Data &y){return x.a<y.a||(x.a==y.a&&x.b<y.b);}
23 bool cmpb(const Data &x,const Data &y){return x.b<y.b||(x.b==y.b&&x.a<y.a);}
24 struct Oper{
25     int u,v,fa,siz,ma,mb;
26 }oper[maxn];
27 void merge(int u,int v,int a,int b){
28     u=find(u),v=find(v);
29     if (siz[u]>siz[v]) swap(u,v);
30     oper[++cnt]=(Oper){u,v,fa[u],siz[v],maxa[v],maxb[v]};
31     if (u==v) maxa[u]=max(maxa[u],a),maxb[u]=max(maxb[u],b);
32     else fa[u]=v,siz[v]+=siz[u],maxa[v]=max(maxa[v],max(maxa[u],a)),maxb[v]=max(maxb[v],max(maxb[u],b));
33 }
34 void undo(){
35     for (;cnt;cnt--){
36         fa[oper[cnt].u]=oper[cnt].fa;
37         maxa[oper[cnt].v]=oper[cnt].ma;
38         maxb[oper[cnt].v]=oper[cnt].mb;
39         siz[oper[cnt].v]=oper[cnt].siz;
40     }
41 }
42 int main(){
43     read(n),read(m),rm=sqrt(m);
44     for (int i=1;i<=m;i++) edge[i].init(i);
45     sort(edge+1,edge+m+1,cmpa);
46     read(q);
47     for (int i=1;i<=q;i++) quer[i].init(i);
48     sort(quer+1,quer+q+1,cmpb);
49     for (int i=1;i<=m;i+=rm){
50         tot=0;
51         for (int j=1;j<=q;j++) if (edge[i].a<=quer[j].a&&(i+rm>m||quer[j].a<edge[i+rm].a)) tmp[++tot]=quer[j];
52         sort(edge+1,edge+i,cmpb);
53         for (int j=1;j<=n;j++) fa[j]=j,siz[j]=1,maxa[j]=maxb[j]=-1;
54         for (int j=1,k=1;j<=tot;j++){
55             for (;k<i&&edge[k].b<=tmp[j].b;k++) merge(edge[k].u,edge[k].v,edge[k].a,edge[k].b);
56             cnt=0;
57             for (int p=i;p<i+rm&&p<=m;p++) if (edge[p].a<=tmp[j].a&&edge[p].b<=tmp[j].b)
58                 merge(edge[p].u,edge[p].v,edge[p].a,edge[p].b);
59             int x=find(tmp[j].u),y=find(tmp[j].v);
60             ans[tmp[j].id]=(x==y&&maxa[x]==tmp[j].a&&maxb[x]==tmp[j].b);
61             undo();
62         }
63     }
64     for (int i=1;i<=q;i++) if (ans[i]) puts("Yes"); else puts("No");
65     return 0;
66 }
时间: 2024-08-29 17:12:33

4537: [Hnoi2016]最小公倍数的相关文章

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之间的路径,使得路径依次经过的边上的权值的最小公

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

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

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

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

【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

2017.7.22 hnoi2016 最小公倍数

#include<cmath> #include<cstdio> #include<iostream> #include<algorithm> #define N 50010 #define M 100010 #define RG register #define inf 0x3f3f3f3f using namespace std; bool ans[N]; struct stac{ int u,v,op; }sta[40000]; struct node

BZOJ4537 : [Hnoi2016]最小公倍数

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

bzoj4537【HNOI2016】最小公倍数

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

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

HDU 1108: 最小公倍数

最小公倍数 #include<iostream> using namespace std; int gcd(int a, int b) { while (b) { int t = a%b; a = b; b = t; } return a; } int lcm(int a, int b) { return a / gcd(a, b)*b; } int main() { ios::sync_with_stdio(false); int a, b; while (cin >> a &g