传送门:【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)
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 0 x = 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 ; }