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