【BZOJ】2001 [Hnoi2010]City 城市建设 cdq分治——动态最小生成树

来源:互联网 发布:微星 主机 知乎 编辑:程序博客网 时间:2024/06/05 10:50

传送门:【BZOJ】2001  [Hnoi2010]City 城市建设


题目分析:今天一中午+一下午的时间全部交代给这题了。。。

两个关键的操作:
Reduction(删除无用边):
把待修改的边标为INF,做一遍MST,把做完后不在MST中的非INF边删去(因为这些边在原图的情况下肯定更不可能选进MST的边集,即无用边);
Contraction(缩必须边,缩点):
把待修改的边标为-INF,做一遍MST,在MST中的非-INF边为必须边(因为这些边在原图的情况下也一定会被选进MST),缩点。
cdq_solve ( l , r ):
    if ( l == r ) {
        求mst,记录结果;
        return ;
    }
    Contraction&Reduction;
    solve ( l , m ) ;
    solve ( m + 1 , r ) ;
end ;
这样不断处理并递归下去到最后一层点数以及边数都很少了。
大概复杂度:T(n) = T(n/2) + O(nlogn) ==> T(n) = O(nlog^2n)

代码如下:


#include <cstdio>#include <vector>#include <cstring>#include <algorithm>using namespace std ; #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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )#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 , cnt#define mid ( ( l + r ) >> 1 )typedef long long LL ;const int MAXN = 20005 ;const int MAXM = 50005 ;const int MAXQ = 50005 ;const int INF = 0x3f3f3f3f ;struct Edge {int u , v , d ;int idx ;bool operator < ( const Edge& a ) const {return d < a.d ;}} E[17][MAXM] , L[MAXM] , tmp[MAXM] ;//E[][]层次图,L为当前层次的图,tmp为中间变量struct ASK {int k , d ;} ask[MAXQ] ;//询问int edge_num[17] , node_num[17] ;//每一层点和边的数量bool change[MAXM] ;//change[] = 1则该边是必须在最小生成树内的边int newv[MAXN] ;//点的新标号int real[MAXM] ;//将边的编号转化为改变后的编号int p[MAXN] , rank[MAXN] ;//并查集专用int val[MAXM] ;//边的价值LL ans[MAXQ] ;//答案int n , m , q ;int find ( int x ) {int o = x , ans , tmp ;while ( o != p[o] ) o = p[o] ;ans = o , o = x ;while ( o != p[o] ) {tmp = p[o] ;p[o] = ans ;o = tmp ;}return ans ;}bool merge ( int x , int y ) {//合并return 1,否则return 0x = find ( x ) ;y = find ( y ) ;if ( x == y ) return 0 ;if ( rank[x] <= rank[y] ) {p[x] = y ;if ( rank[x] == rank[y] ) ++ rank[y] ;} else p[y] = x ;return 1 ;}void clear ( int n ) {FOR ( i , 1 , n ) p[i] = i , rank[i] = 0 ;}void contraction ( int& NV , int& NE , LL& res ) {int NV_2 = 0 , NE_2 = 0 ;clear ( NV ) ;REP ( i , 0 , NE ) change[i] = 0 ;sort ( L , L + NE ) ;REP ( i , 0 , NE ) {if ( merge ( L[i].u , L[i].v ) && L[i].d != -INF ) {//必须在树中的边res += L[i].d ;change[i] = 1 ;} else tmp[NE_2 ++] = L[i] ;}clear ( NV ) ;REP ( i , 0 , NE ) if ( change[i] ) merge ( L[i].u , L[i].v ) ;FOR ( i , 1 , NV ) if ( find ( i ) == i ) newv[i] = ++ NV_2 ;//点重标号FOR ( i , 1 , NV ) newv[i] = newv[find ( i )] ;REP ( i , 0 , NE_2 ) {L[i] = tmp[i] ;real[L[i].idx] = i ;//修改边的实际对应编号L[i].u = newv[L[i].u] ;L[i].v = newv[L[i].v] ;}NV = NV_2 ;NE = NE_2 ;}void reduction ( int& NV , int& NE ) {int NE_2 = 0 ;clear ( NV ) ;sort ( L , L + NE ) ;REP ( i , 0 , NE ) if ( merge ( L[i].u , L[i].v ) || L[i].d == INF ) {//保存可以变成树边的边real[L[i].idx] = NE_2 ;//修改边的实际对应编号L[NE_2 ++] = L[i] ;}NE = NE_2 ;}void cdq_solve ( int l , int r , int cur , LL res ) {int NV = node_num[cur] ;//当前层次图的点数int NE = edge_num[cur] ;//当前层次图的边数if ( l == r ) val[ask[l].k] = ask[l].d ;//修改边权REP ( i , 0 , NE ) {E[cur][i].d = val[E[cur][i].idx] ;L[i] = E[cur][i] ;real[L[i].idx] = i ;}if ( l == r ) {//只有一个查询操作,直接MST求解clear ( NV ) ;sort ( L , L + NE ) ;REP ( i , 0 , NE ) if ( merge ( L[i].u , L[i].v ) ) res += L[i].d ;ans[l] = res ;return ;}FOR ( i , l , r ) L[real[ask[i].k]].d = -INF ;contraction ( NV , NE , res ) ;//将必须在最小生成树内的边缩去FOR ( i , l , r ) L[real[ask[i].k]].d =  INF ;reduction ( NV , NE ) ;//将一定不在最小生成树内的边删除node_num[cur + 1] = NV ;//保存下一层次图的点数edge_num[cur + 1] = NE ;//保存下一层次图的边数REP ( i , 0 , NE ) E[cur + 1][i] = L[i] ;//建立下一层次图int m = ( l + r ) >> 1 ;cdq_solve ( l , m , cur + 1 , res ) ;cdq_solve ( m + 1 , r , cur + 1 , res ) ;}void solve () {REP ( i , 0 , m ) {scanf ( "%d%d%d" , &E[0][i].u , &E[0][i].v,  &E[0][i].d ) ;val[i] = E[0][i].d ;E[0][i].idx = i ;}FOR ( i , 1 , q ) {scanf ( "%d%d" , &ask[i].k , &ask[i].d ) ;-- ask[i].k ;}node_num[0] = n ;edge_num[0] = m ;cdq_solve ( 1 , q , 0 , 0 ) ;FOR ( i , 1 , q ) printf ( "%lld\n" , ans[i] ) ;}int main () {scanf ( "%d%d%d" , &n , &m , &q ) ;solve () ;return 0 ;}


1 0
原创粉丝点击