P4246 [SHOI2008]堵塞的交通
题意简述
维护一个 \(2*n\) 的网格图的动态连通性
思路
既然是动态连通性,那么我们直接离线线段树分治+可撤销并查集
上面的做法太暴力了,我们考虑分析一些性质
注意到联通的信息是可以合并的,可以考虑使用线段树维护
一个想法是维护区间 左上/左下 到 右上/右下 的连通性
但这样忽略了一种情况:
(1,1) - (1,2) (1,3) - (1,4)
| |
(2,1) - (2,2) - (2,3) - (2,4)
也就是说,我们可以通过询问区间 左边/右边 部分实现行的改变
那么再维护两个信息 \(l,r\) 表示区间 左上左下 , 右上右下 的连通性
合并区间和查询时枚举中间点即可
#include <cstdio>
#include <iostream>
using namespace std;
const int MAXN = 1e5;
int n , v[ MAXN + 5 ] , r[ 2 ][ MAXN + 5 ];
struct node { bool l , r; bool chk[ 2 ][ 2 ]; };
struct Segment_Tree {
node tr[ 4 * MAXN + 5 ];
#define ls x << 1
#define rs x << 1 | 1
#define mid ( l + r >> 1 )
node Merge( node p , node q , int md ) {
node S = {};
for( int i = 0 ; i <= 1 ; i ++ )
for( int j = 0 ; j <= 1 ; j ++ )
for( int k = 0 ; k <= 1 ; k ++ )
S.chk[ i ][ j ] |= p.chk[ i ][ k ] & r[ k ][ md ] & q.chk[ k ][ j ];
S.l = p.l | ( p.chk[ 0 ][ 0 ] && p.chk[ 1 ][ 1 ] && r[ 0 ][ md ] & r[ 1 ][ md ] & q.l );
S.r = q.r | ( q.chk[ 0 ][ 0 ] && q.chk[ 1 ][ 1 ] && r[ 0 ][ md ] & r[ 1 ][ md ] & p.r );
return S;
}
void Build( int x , int l = 1 , int r = n ) {
if( l == r ) { tr[ x ].chk[ 0 ][ 0 ] = tr[ x ].chk[ 1 ][ 1 ] = 1; return; }
Build( ls , l , mid ); Build( rs , mid + 1 , r );
}
void Update( int x , int pos , int l = 1 , int r = n ) {
if( pos < l || pos > r ) return;
if( l == r ) { tr[ x ].chk[ 0 ][ 1 ] = tr[ x ].chk[ 1 ][ 0 ] = tr[ x ].l = tr[ x ].r = v[ l ]; return; }
Update( ls , pos , l , mid ); Update( rs , pos , mid + 1 , r );
tr[ x ] = Merge( tr[ ls ] , tr[ rs ] , mid );
}
node Query( int x , int ql , int qr , int l = 1 , int r = n ) {
if( ql <= l && r <= qr ) return tr[ x ];
if( qr <= mid ) return Query( ls , ql , qr , l , mid );
if( ql > mid ) return Query( rs , ql , qr , mid + 1 , r );
return Merge( Query( ls , ql , qr , l , mid ) , Query( rs , ql , qr , mid + 1 , r ) , mid );
}
}Tr;
char s[ 10 ];
int main( ) {
scanf("%d",&n);
Tr.Build( 1 );
for( int r1 , c1 , r2 , c2 ; scanf("%s", s ) && s[ 0 ] != 'E' ; ) {
scanf("%d %d %d %d",&r1,&c1,&r2,&c2); r1 -- , r2 --;
if( s[ 0 ] == 'O' ) {
if( r1 == r2 ) { if( c1 > c2 ) swap( c1 , c2 ); r[ r1 ][ c1 ] = 1; }
else v[ c1 ] = 1;
Tr.Update( 1 , c1 );
}
if( s[ 0 ] == 'C' ) {
if( r1 == r2 ) { if( c1 > c2 ) swap( c1 , c2 ); r[ r1 ][ c1 ] = 0; }
else v[ c1 ] = 0;
Tr.Update( 1 , c1 );
}
if( s[ 0 ] == 'A' ) {
bool ans = 0;
if( c1 > c2 ) swap( c1 , c2 ) , swap( r1 , r2 );
node S = Tr.Query( 1 , c1 , c2 ) , L = Tr.Query( 1 , 1 , c1 ) , R = Tr.Query( 1 , c2 , n );
ans |= S.chk[ r1 ][ r2 ];
ans |= S.chk[ r1 ][ !r2 ] & R.l;
ans |= L.r & S.chk[ !r1 ][ r2 ];
ans |= L.r & S.chk[ !r1 ][ !r2 ] & R.l;
puts( ans ? "Y" : "N" );
}
}
return 0;
}