BZOJ4537 [Hnoi2016]最小公倍数 分块+可撤销并查集

9 篇文章 0 订阅
9 篇文章 0 订阅

大家都很强, 可与之共勉。

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" ) ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值