zoukankan      html  css  js  c++  java
  • [HNOI2016]最小公倍数

    题目

      点这里看题目。

    分析

      一类比较经典的分块优化暴力的思路。
      问题实际上是查询,当(ale Qa, ble Qb)的所有边都插入了图之后,(u,v)是否连通,并且(u,v)的连通块里面是否同时存在(a=Qa)(b=Qb)的边。
      以上信息可以用并查集来维护。
      问题的瓶颈是,如何快速地提取出需要的边。
      我们不妨考虑这样一个算法:
      将边按照(a)排序并且分块处理,块的大小是(T)。对于当前块([i,i+T)),我们处理(Qa)([a_i,a_{i+T}))之间的询问。
      现在考虑处理掉(b)。发现对于([1,i))的边,当前询问只要求它(ble Qb)。因此我们可以让([1,i))的边按照(b)来排序,并且让所有询问按照(Qb)来排序。这样对于([1,i))的边,(b)的限制就可以用指针方便地维护了。
      再考虑([i,i+T))的边,由于它们的数量比较少,所以我们可以直接暴力插入合法边,查询完之后再删除。由于这里存在并查集的删除操作,因此我们需要用到可回退的并查集,配合秩优化。
      时间复杂度:
        多次排序:(O(frac{m^2}Tlog_2m))
        块外边:(O(frac{m^2}Tlog_2n))
        块内边:(O(TQlog_2n))
      总时间大概是......(O(frac{m^2}Tlog_2n+frac{mQ}Tlog_2n))
      根据均值不等式可以得到,(T=sqrt{frac{m^2}Q})是比较 OK 的吧......
      但是经过评测,(T=sqrt{mlog_2n})的时候是比较优秀的。

    代码

    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 1e5 + 5, MAXM = 2e5 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    template<typename _T>
    void swapp( _T &x, _T &y )
    {
    	_T t = x; x = y, y = t;
    }
    
    struct element
    {
    	int u, v, a, b, s;
    	element() { u = v = a = b = s = 0; }
    	void get() { read( u ), read( v ), read( a ), read( b ); }
    	element( const int U, const int V, const int A, const int B, const int S ) { u = U, v = V, a = A, b = B, s = S; }
    }E[MAXM], q[MAXN], lst[MAXM];
    
    int fa[MAXN], siz[MAXN], mxA[MAXN], mxB[MAXN];
    int seq[MAXM];
    int N, M, Q, blk, top;
    bool ans[MAXN];
    
    void upt( int &x, const int v ) { x = MAX( x, v ); }
    bool cmp1( const element &x, const element &y ) { return x.a < y.a; }
    bool cmp2( const element &x, const element &y ) { return x.b < y.b; }
    int findSet( const int u ) { return u == fa[u] ? u : findSet( fa[u] ); }
    void makeSet( const int n ) { for( int i = 1 ; i <= n ; i ++ ) fa[i] = i, siz[i] = 1, mxA[i] = mxB[i] = -1; }
    
    void unionSet( int u, int v, const int A, const int B )
    {
    	u = findSet( u ), v = findSet( v );
    	if( siz[u] > siz[v] ) swapp( u, v );
    	lst[++ top] = element( u, v, mxA[v], mxB[v], siz[v] );
    	if( u ^ v ) 
    		fa[u] = v, siz[v] += siz[u], 
    		upt( mxA[v], mxA[u] ), upt( mxB[v], mxB[u] );
    	upt( mxA[v], A ), upt( mxB[v], B );
    }
    
    bool chk( int u, int v, const int A, const int B )
    {
    	u = findSet( u ), v = findSet( v );
    	return u == v && mxA[u] == A && mxB[u] == B;
    }
    
    int main()
    {
    	read( N ), read( M );
    	for( int i = 1 ; i <= M ; i ++ ) E[i].get();
    	read( Q ), blk = sqrt( M * log2( N ) );
    	for( int i = 1 ; i <= Q ; i ++ ) q[i].get(), q[i].s = i;
    	sort( E + 1, E + 1 + M, cmp1 );
    	sort( q + 1, q + 1 + Q, cmp2 );
    	int tot = 0, p, cur;
    	for( int i = 1 ; i <= M ; i += blk )
    	{
    		makeSet( N ), tot = 0;
    		if( i > 1 ) std :: sort( E + 1, E + i, cmp2 );
    		for( int k = 1 ; k <= Q ; k ++ )
    			if( E[i].a <= q[k].a && ( q[k].a < E[i + blk].a || i + blk > M ) )
    				seq[++ tot] = k;
    		p = 1;
    		for( int k = 1 ; k <= tot ; k ++ )
    		{
    			cur = seq[k];
    			for( ; p < i && E[p].b <= q[cur].b ; p ++ ) 
    				unionSet( E[p].u, E[p].v, E[p].a, E[p].b );
    			top = 0;
    			for( int j = i ; j < i + blk && j <= M ; j ++ )
    				if( E[j].a <= q[cur].a && E[j].b <= q[cur].b )
    					unionSet( E[j].u, E[j].v, E[j].a, E[j].b );
    			ans[q[cur].s] = chk( q[cur].u, q[cur].v, q[cur].a, q[cur].b );
    			while( top )
    			{
    				int u = lst[top].u, v = lst[top].v; fa[u] = u;
    				mxA[v] = lst[top].a, mxB[v] = lst[top].b, siz[v] = lst[top].s;
    				top --;
    			}
    		}
    	}
    	for( int i = 1 ; i <= Q ; i ++ ) puts( ans[i] ? "Yes" : "No" );
    	return 0;
    }
    
  • 相关阅读:
    Qual IPE中的Crop计算
    TinyCC安装
    C编译器(TCC)
    Macros之PRId64
    shell脚本学习 (10) 从结构化文本提取数据
    shell脚本学习 (9) 提取开头或结尾的几行
    shell脚本学习 (8) fmt 格式化段落
    接触python的第2天:了解变量和打印
    接触python的第1天:测试hello world
    shell脚本学习(7)sort
  • 原文地址:https://www.cnblogs.com/crashed/p/13033925.html
Copyright © 2011-2022 走看看