HNOI 2016 最小公倍数(分块+并查集)

[Hnoi2016 day1]最小公倍数

问题描述

给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成$2^a3^b$的形式。现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为$2^a3^b$。注意:路径可以不是简单路径。下面是一些可能有用的定义:最小公倍数:K个数$a_1,a_2,…,a_k$的最小公倍数是能被每个$a_i$整除的最小正整数。路径:路径$P:P_1,P_2,…,P_k$是顶点序列,满足对于任意$1<=i<k$,节点$P_i$和$P_{i+1}$之间都有边相连。简单路径:如果路径$P:P_1,P_2,…,P_k$中,对于任意$1<=s≠t<=k$都有$P_s≠P_t$,那么称路径为简单路径。

输入格式

第一行包含两个整数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$

输出格式

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

样例输入

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

样例输出

Yes

Yes

Yes

No

No


首先容易想到一个朴素的暴力,即对每个询问,将满足$a_i<=a\&\&b_i<=b$的所有边拿来做一次并查集,然后如果$u,v$在同一个集合中,且这个集合中$Maxa==a\&\&Maxb==b$,那么就存在可行路径。这样做是$O(qm)$的,可以用分块优化。

考虑将边按照$a_i$排序后分组,分成$\sqrt{m}$组,将每个询问插入到对应的组中,对应的组的意思是对于组$i$中第一个元素$a_{询问}>=a_i$,且对于组$i+1$的第一个元素,$a_{询问}<a_{i+1}$

这样分组后,依次处理每一组的询问,注意到对于组$i$中的询问,前$i-1$组的$a_i$值一定是满足要求的,因此只需要将前$i-1$组中$b_i$也满足要求的插入并查集,因此可以将询问和前$i-1$组的边按照$b_i$排序,这样就可以每组询问$O(m)$处理了,然后对于同组中的边,直接暴力判断是否加入并查集中,然后处理下一个询问时还原并查集,这样的话每个询问的复杂度是$O(\sqrt{m})$的

然后这里的排序可以利用归并排序优化,总复杂度$O(m\sqrt{m}+m\log m)$


代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>

#define N 100005
using namespace std;
char *p1,*p2,buf[1<<20];
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?0:*p1++)
inline void _R(int &x)
{
char t=GC;bool f=0;
while(t!='-'&&(t<48||t>57))t=GC;
if(t=='-')f=1,t=GC;
for(x=0;t>47&&t<58;t=GC)x=(x<<1)+(x<<3)+t-48;
if(f)x=-x;
}
struct nodd{int x,f,a,b,d;}S[N<<3];
struct node{int x,y,a,b,id;}E[N],K[N],T[N],C[N];
int n,m,q,Ans[N],lp[N],rp[N],Si,id[N],F[N],si[N],top,tot,Maxa[N],Maxb[N];
vector<node>Q[N];
bool cmp1(node a,node b)
{
if(a.a==b.a)return a.b<b.b;
return a.a<b.a;
}
bool cmp2(node a,node b)
{
if(a.b==b.b)return a.a<b.a;
return a.b<b.b;
}
int GF(int x){return F[x]!=x?GF(F[x]):x;}
int GM(int x,int y,int z)
{
if(x>=y&&x>=z)return x;
if(y>=z)return y;
return z;
}
void Merge(int x,int y,int a,int b)
{
int fx=GF(x),fy=GF(y);
S[++top]=(nodd){fx,F[fx],Maxa[fx],Maxb[fx],si[x]};
S[++top]=(nodd){fy,F[fy],Maxa[fy],Maxb[fy],si[y]};
if(fx==fy)
{
Maxa[fx]=max(Maxa[fx],a);
Maxb[fx]=max(Maxb[fx],b);
return;
}
if(si[fx]>si[fy])swap(fx,fy);
F[fx]=fy;
Maxa[fy]=GM(Maxa[fx],Maxa[fy],a);
Maxb[fy]=GM(Maxb[fy],Maxb[fx],b);
si[fy]+=si[fx];
}
void Mergesort(int l,int r)
{
int i=1,j=l,k=0;
while(i<=tot&&j<=r)
{
if(T[i].b<=E[j].b)C[++k]=T[i++];
else C[++k]=E[j++];
}
while(i<=tot)C[++k]=T[i++];
while(j<=r)C[++k]=E[j++];
for(i=1,tot=k;i<=k;i++)T[i]=C[i];
}
void Recover(int x)
{
F[S[x].x]=S[x].f;
Maxa[S[x].x]=S[x].a;
Maxb[S[x].x]=S[x].b;
si[S[x].x]=S[x].d;
}
int main()
{
int i,j,k,x,y,a,b,las,p;
_R(n);_R(m);
Si=sqrt(m);
for(i=1;i<=m;i++)
{
_R(x);_R(y);_R(a);_R(b);
E[i]=(node){x,y,a,b,0};
}
_R(q);
for(i=1;i<=q;i++)
{
_R(x);_R(y);_R(a);_R(b);
K[i]=(node){x,y,a,b,i};
}
sort(E+1,E+m+1,cmp1);
for(i=1;i<=m;i++)
{
id[i]=i/Si+1;
if(!lp[id[i]])lp[id[i]]=i;
rp[id[i]]=max(rp[id[i]],i);
}
E[m+1].a=1e9+7;lp[id[m]+1]=m+1;
for(i=1;i<=q;i++)
for(j=1;j<=id[m];j++)if(K[i].a<E[lp[j+1]].a){Q[j].push_back(K[i]);break;}
for(i=1;i<=id[m];i++)
{
sort(E+lp[i],E+rp[i]+1,cmp2);
sort(Q[i].begin(),Q[i].end(),cmp2);
}
for(i=1;i<=id[m];i++)
{
for(j=1;j<=n;j++)F[j]=j,si[j]=1,Maxa[j]=Maxb[j]=-1;
for(j=0,p=1,top=0;j<Q[i].size();j++)
{
while(p<=tot&&T[p].b<=Q[i][j].b)Merge(T[p].x,T[p].y,T[p].a,T[p].b),p++;
for(las=top,k=lp[i];k<=rp[i]&&E[k].b<=Q[i][j].b;k++)if(E[k].a<=Q[i][j].a)Merge(E[k].x,E[k].y,E[k].a,E[k].b);
x=GF(Q[i][j].x);y=GF(Q[i][j].y);
if(x==y&&Maxa[x]==Q[i][j].a&&Maxb[x]==Q[i][j].b)Ans[Q[i][j].id]=1;
while(top!=las)Recover(top--);
}
Mergesort(lp[i],rp[i]);
}
for(i=1;i<=q;i++)Ans[i]?puts("Yes"):puts("No");
}