大家都很强, 可与之共勉。
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
题解
考虑暴力做法,就是把所有<=a且<=b的边全部用并查集维护,记录每个连通块ab的最大值。
想到这个做法,不难想到分块,把边按照a为第一关键字,b为第二关键字排序,分成若干块。
每次把前i块的边和第i块的询问按照b的大小排序,按照b的权值从小到大把边加入并查集,注意如果这条边的a权值>这一块的询问的最小a权值,那么不加入,而是开一个队列记录这些边,这些边最多有块的大小个。
然后对于每个询问,把队列里a<=询问的a的加入并查集,查询结束后删除之。
对于每个询问,
将所有a和b小于等于询问值的的边加入图中(用并查集),
如果询问的u和v在一个联通块中,
且该联通块的maxa和maxb均等与询问的a和b,
则答案为Yes。
补充
将所有边按a值升序排序,分成√m 块操作,
设每块第一条边为sp,每块长度为len,
每次操作将edge[sp].a<=a
/**************************************************************
Problem: 4537
User: Lazer2001
Language: C++
Result: Accepted
Time:23172 ms
Memory:5076 kb
****************************************************************/
# include <bits/stdc++.h>
inline int readInt ( ) {
register int x, c ;
while ( ! isdigit ( c = getchar ( ) ) ) ;
for ( x = -48 + c ; isdigit ( c = getchar ( ) ) ; ( x *= 10 ) += c - 48 ) ;
return x ;
}
struct Edge {
int u, v, a, b ;
inline void PutIn ( ) {
u = readInt ( ), v = readInt ( ), a = readInt ( ), b = readInt ( ) ;
}
inline bool operator < ( const Edge& rhs ) const {
return a < rhs.a ;
}
} eg [100010], q [50010] ;
int ep [100010], qp [50010] ;
inline bool cmp ( int a, int b ) {
return eg [a].b < eg [b].b ;
}
inline bool _cmp_ ( int a, int b ) {
return q [a].b < q [b].b ;
}
class UFS {
private :
std :: stack < std :: pair < int*, int > > stk ;
int *fa, *rank, n ;
public :
int *va, *vb ;
UFS ( ) { }
UFS ( int n ) {
this -> n = n ;
fa = new int [( const int ) n + 1] ;
rank = new int [( const int ) n + 1] ;
va = new int [( const int ) n + 1] ;
vb = new int [( const int ) n + 1] ;
}
inline void set ( ) {
//memset ( va, -1, sizeof va ) ;
//memset ( vb, -1, sizeof vb ) ; // Not Able To Fill value -1 ;
while ( ! stk.empty ( ) ) stk.pop ( ) ;
for ( register int i = 1 ; i <= n ; ++ i ) va [i] = vb [i] = -1 ; //0个数的最大公约数不是1
memset ( rank, 0, sizeof rank ) ;
for ( register int i = 1 ; i <= n ; ++ i ) fa [i] = i ;
}
inline int find ( int x ) {
while ( x ^ fa [x] ) x = fa [x] ;
return x ;
}
inline void Join ( Edge& e, const bool& del ) {
int x = find ( e.u ), y = find ( e.v ) ;
//if ( x == y ) return ; !!!//这样无法更新块内信息!
if ( x ^ y ) {
if ( rank [x] > rank [y] ) x ^= y ^= x ^= y ;
if ( del ) stk.push ( std :: make_pair ( fa + x, fa [x] ) ) ;
fa [x] = y ;
if ( rank [x] == rank [y] ) {
if ( del ) stk.push ( std :: make_pair ( rank + y, rank [y] ) ) ;
++ rank [y] ;
}
}
int tmp = std :: max ( va [x], e.a ) ;
if ( tmp > va [y] ) {
if ( del ) stk.push ( std :: make_pair ( va + y, va [y] ) ) ;
va [y] = tmp ;
}
tmp = std :: max ( vb [x], e.b ) ;
if ( tmp > vb [y] ) {
if ( del ) stk.push ( std :: make_pair ( vb + y, vb [y] ) ) ;
vb [y] = tmp ;
}
}
inline void clear ( ) {
while ( ! stk.empty ( ) ) {
*stk.top ( ).first = stk.top ( ).second ;
stk.pop ( ) ;
}
}
} T ;
bool ans [50010] ;
int main ( ) {
int n ( readInt ( ) ), m ( readInt ( ) ) ;
T = UFS ( n ) ;
for ( int i = 1 ; i <= m ; ++ i ) eg [i].PutIn ( ) ;
for ( int i = 1 ; i <= m ; ++ i ) ep [i] = i ;
int Q ( readInt ( ) ) ;
for ( int i = 1 ; i <= Q ; ++ i ) q [i].PutIn ( ) ;
std :: sort ( eg + 1, eg + 1 + m ) ;
int blk = sqrt ( m ) ;
for ( int sq = 1 ; sq <= m ; sq += blk ) {
int cnt ( 0 ) ;
for ( int i = 1 ; i <= Q ; ++ i )
if ( q [i].a >= eg [sq].a && ( q [i].a < eg [sq + blk].a || sq + blk > m ) ) {
qp [++ cnt] = i ;
}
if ( ! cnt ) continue ;
std :: sort ( ep + 1 , ep + 1 + sq, cmp ) ;
std :: sort ( qp + 1, qp + 1 + cnt, _cmp_ ) ;
T.set ( ) ;
for ( int i = 1, ne = 1 ; i <= cnt ; ++ i ) {
while ( ne <= sq && eg [ep [ne]].b <= q [qp [i]].b ) {
T.Join ( eg [ep [ne ++]], 0 ) ;
}
for ( int j = sq, l = std :: min ( m + 1, sq + blk ) ; j < l ; ++ j )
if ( eg [ep [j]].a <= q [qp [i]].a && eg [ep [j]].b <= q [qp [i]].b ) {
T.Join ( eg [ep [j]], 1 ) ;
}
int u = T.find ( q [qp [i]].u ), v = T.find ( q [qp [i]].v ) ;
ans [qp [i]] = ( u == v && T.va [u] == q [qp [i]].a && T.vb [u] == q [qp [i]].b ) ;
T.clear ( ) ;
}
}
for ( int i = 1 ; i <= Q ; ++ i ) {
puts ( ans [i] ? "Yes" : "No" ) ;
}
}