传送门:【HDU】4677 Query on Graph
题目分析:
首先将询问离线。
将所有的边固定形式:左端点小,右端点大。
将边按照右端点从小到大排序,将询问按照右端点从小到大排序。
然后从左到右遍历询问,将边的右端点小于等于询问右端点的都插入到动态树上。
因为动态树要保证是一棵树,于是我们要在加一条边成环的时候去掉环上的一条边来保证动态树依旧是树结构。注意到加边成环时再加一条边并减去一条边不会改变连通块的个数。去边时,我们选择边的左端点最小的边来从树上删除。为什么选择删除左端点最小的呢?因为当查询为【L,R】时,如果最小左端点为L1<L,次小左端点为L2>L,注意到我们删除L1或L2形成的连通块其实是同一个,那么左端点大的优先被保留一定不会使结果变差,这样更容易被【L,R】包含在内。然后添加了一条左端点为u的边到树中就用树状数组单点更新,删除同理。最后查询的时候便查询树状数组内【L,R】有多少条边,注意到右端点大于R的都还没被更新,而边又是根据左端点插入的(左端点小于右端点,这个是我们一开始就约定的),那么【L,R】内存在的边一定是连接【L,R】内点的边,在【L,R】内没有边的时候连通块的个数为R-L+1,没增加一条边连通块个数就-1,所以此时查询的答案就是 {(R-L+1)-【L,R】内边的个数 }(注意这些边最多只会形成树,不会形成环,因为如果有环的话,通过动态树我们就会删除左端点最小的边来维护成一棵树)。
这里有一个思想贯穿主线:保留对我们有用的最近时刻的信息。
代码如下:
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> using namespace std ; typedef long long LL ; #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define clr( a , x ) memset ( a , x , sizeof a ) #define cpy( a , x ) memcpy ( a , x , sizeof a ) #define ls ( o << 1 ) #define rs ( o << 1 | 1 ) #define lson ls , l , m #define rson rs , m + 1 , r #define root 1 , 1 , n #define mid ( ( l + r ) >> 1 ) const int MAXN = 30005 ; const int MAXE = 100005 ; const int INF = 0x3f3f3f3f ; struct Seg { int l , r , idx ; Seg () {} Seg ( int l , int r , int idx ) : l ( l ) , r ( r ) , idx ( idx ) {} bool operator < ( const Seg& a ) const { return r < a.r ; } } ; struct Edge { int u , v ; Edge () {} Edge ( int u , int v ) : u ( u ) , v ( v ) {} bool operator < ( const Edge& a ) const { return v < a.v ; } } ; struct Node* null ; struct Node { Node* c[2] ; Node* f ; int minv , v ; int minidx , idx ; int flip ; inline void newnode ( int val , int index ) { minv = v = val ; minidx = idx = index ; flip = 0 ; c[0] = c[1] = f = null ; } inline void reserve () { if ( this == null ) return ; swap ( c[0] , c[1] ) ; flip ^= 1 ; } inline void push_up () { if ( this == null ) return ; minidx = idx ; minv = v ; if ( minv > c[0]->minv ) { minv = c[0]->minv ; minidx = c[0]->minidx ; } if ( minv > c[1]->minv ) { minv = c[1]->minv ; minidx = c[1]->minidx ; } } inline void push_down () { if ( this == null ) return ; if ( flip ) { c[0]->reserve () ; c[1]->reserve () ; flip = 0 ; } } inline void setc ( Node* o , int d ) { c[d] = o ; o->f = this ; } inline bool is_root () { return f == null || f->c[0] != this && f->c[1] != this ; } inline void sign_down () { if ( !is_root () ) f->sign_down () ; push_down () ; } inline void rot ( int d ) { Node* p = f ; Node* g = f->f ; p->setc ( c[d] , !d ) ; if ( !p->is_root () ) g->setc ( this , f == g->c[1] ) ; else f = g ; setc ( p , d ) ; p->push_up () ; } inline Node* splay () { sign_down () ; while ( !is_root () ) { if ( f->is_root () ) rot ( this == f->c[0] ) ; else { if ( f == f->f->c[0] ) { if ( this == f->c[0] ) f->rot ( 1 ) , rot ( 1 ) ; else rot ( 0 ) , rot ( 1 ) ; } else { if ( this == f->c[1] ) f->rot ( 0 ) , rot ( 0 ) ; else rot ( 1 ) , rot ( 0 ) ; } } } push_up () ; return this ; } inline Node* access () { Node* o = this ; Node* x = null ; for ( ; o != null ; x = o , o = o->f ) { o->splay () ; o->setc ( x , 1 ) ; o->push_up () ; } return splay () ; } inline void make_root () { access ()->reserve () ; } inline Node* find_root () { Node* o = access () ; for ( ; o->c[0] != null ; o = o->c[0] ) o->push_down () ; //printf ( "233\n" ) ; return o ; } inline void link ( Node* o ) { if ( this == o || find_root () == o->find_root () ) return ; make_root () ; f = o ; } inline void cut () { access () ; c[0]->f = null ; c[0] = null ; push_up () ; } inline void cut ( Node* o ) { if ( find_root () != o->find_root () ) return ; make_root () ; o->cut () ; } inline int query ( Node* o ) { if ( find_root () != o->find_root () ) return 0 ; access ()->make_root () ; return o->access ()->minidx ; } } ; Node pool[MAXN + MAXE] ; Node* cur ; Node* node[MAXN] ; Node* edge[MAXE] ; Seg Q[MAXN] ; Edge E[MAXE] ; int T[MAXN] ; int ans[MAXN] ; int n , m , q ; void clear () { cur = pool ; null = cur ++ ; null->newnode ( INF , INF ) ; clr ( T , 0 ) ; } inline void add ( int x , int v ) { for ( int i = x ; i <= n ; i += i & -i ) T[i] += v ; } inline int sum ( int x , int ans = 0 ) { for ( int i = x ; i > 0 ; i -= i & -i ) ans += T[i] ; return ans ; } inline void scanf ( int& x , char c = 0 ) { while ( ( c = getchar () ) < '0' ) ; x = c - '0' ; while ( ( c = getchar () ) >= '0' ) x = x * 10 + c - '0' ; } void solve () { int u , v ; int l , r ; clear () ; scanf ( n ) ; scanf ( m ) ; For ( i , 1 , n ) { node[i] = cur ++ ; node[i]->newnode ( INF , INF ) ; } For ( i , 1 , m ) { scanf ( u ) ; scanf ( v ) ; if ( u > v ) swap ( u , v ) ; E[i] = Edge ( u , v ) ; } scanf ( q ) ; For ( i , 1 , q ) { scanf ( l ) ; scanf ( r ) ; Q[i] = Seg ( l , r , i ) ; } sort ( E + 1 , E + m + 1 ) ; sort ( Q + 1 , Q + q + 1 ) ; For ( i , 1 , m ) { edge[i] = cur ++ ; edge[i]->newnode ( E[i].u , i ) ; } int j = 1 ; For ( i , 1 , q ) { while ( j <= m && E[j].v <= Q[i].r ) { int u = E[j].u ; int v = E[j].v ; int x = node[u]->query ( node[v] ) ; if ( x ) { edge[x]->cut ( node[E[x].u] ) ; edge[x]->cut ( node[E[x].v] ) ; add ( E[x].u , -1 ) ; } edge[j]->link ( node[u] ) ; edge[j]->link ( node[v] ) ; add ( u , 1 ) ; ++ j ; } ans[Q[i].idx] = ( Q[i].r - Q[i].l + 1 ) - ( sum ( Q[i].r ) - sum ( Q[i].l - 1 ) ) ; } For ( i , 1 , q ) printf ( "%d\n" , ans[i] ) ; } int main () { int T , cas = 0 ; scanf ( T ) ; while ( T -- ) { printf ( "Case #%d:\n" , ++ cas ) ; solve () ; } return 0 ; }