【codeforces】2014-2015ACM-ICPC CERC 14 Problem J: Pork barrel 【LCT+主席树】

传送门:【codeforces】2014-2015ACM-ICPC CERC 14 Problem J: Pork barrel


题目分析:pushup内写错了一直没发现。。。把maxidx写成idx然后查了一下午没查出来,到晚饭后才发现。。然后再纠正了数组大小,终于AC了。。。写了我一下午,就因为这么一个小错误T  T


今天是平安夜,祝大家平安夜快乐!


这题是要维护一个最小生成树的构造过程。

首先,我们假设这题可以离线!

我们先将边从大到小排序,然后依次连接两点,如果某一次枚举的边的两点已经在一条链上,那么我们删除这个链上权值最大的边,然后添加这次枚举的边,这样我们既保证了连通块的数量不会增加,同时维护这么多连通块的花费也不会增加。然后我们用到了第i小的边,那么在区间中第i个位置加上这条边的权值,删除的时候就减小。

此时我们注意,当存在询问【L,R】时,如果当前枚举的边是大于等于L的最小的边,那么这时我们决定处理这个询问【L,R】。此时用到的边的最小值就是区间内【L,R】的权值和!这个我们可以用树状数组维护,且由于【1,L-1】都还没被插入,所以其实就是【1,R】内的权值和。大于R的如果是被删除的那么本来就不需要,如果是树边则只能不是用它而增加连通块个数(因为被限制只能使用权值【L,R】内的边),小于等于R的无论怎么和权值稍大且同样小于等于R的被删除的边交换,连通块个数也不会减少,因为即使没有小于等于R的限制这些边一样要删除(可能我这样描述也不太正确,抱歉T  T)。进行操作前还需要将询问按照左端点从大到小排个序。


现在问题要求强制在线,那么要么用可持久化树状数组(不知道有没有,反正我不会),要么可以用主席树(可持久化线段树)。这里我是选择主席树了。

除了用主席树保存历史记录,其他都和离线的差不多。


代码如下:


#include <cstdio>
#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 lson l , m
#define rson m + 1 , r
#define mid ( ( l + r ) >> 1 )

const int MAXN = 1005 ;
const int MAXE = 100005 ;
const int INF = 0x3f3f3f3f ;

namespace PST {
	struct Node {
		Node* c[2] ;
		int sum ;

		inline void pushup () {
			sum = c[0]->sum + c[1]->sum ;
		}
	} ;

	Node pool[MAXE * 40] ;
	Node* node[MAXE] ;
	Node* cur ;

	inline void init () {
		cur = pool ;
	}

	inline void build ( Node* &o , int l , int r ) {
		o = cur ++ ;
		o->sum = 0 ;
		if ( l == r ) return ;
		int m = mid ;
		build ( o->c[0] , lson ) ;
		build ( o->c[1] , rson ) ;
	}

	inline void insert ( Node* old , Node* &now , int x , int v , int l , int r ) {
		now = cur ++ ;
		if ( l == r ) {
			now->sum = old->sum + v ;
			return ;
		}
		int m = mid ;
		if ( x <= m ) {
			now->c[1] = old->c[1] ;
			insert ( old->c[0] , now->c[0] , x , v , lson ) ;
		} else {
			now->c[0] = old->c[0] ;
			insert ( old->c[1] , now->c[1] , x , v , rson ) ;
		}
		now->pushup () ;
	}

	inline int query ( Node* now , int x , int l , int r ) {
		int ans = 0 ;
		while ( l < r ) {
			int m = mid ;
			if ( x <= m ) {
				now = now->c[0] ;
				r = m ;
			} else {
				ans += now->c[0]->sum ;
				now = now->c[1] ;
				l = m + 1 ;
			}
		}
		ans += now->sum ;
		return ans ;
	}
} ;

namespace LCT {
	struct Node* null ;
	struct Node {
		Node* c[2] ;
		Node* f ;
		int flip ;
		int maxv , v ;
		int maxidx , idx ;

		inline void newnode ( int val , int id ) {
			c[0] = c[1] = f = null ;
			flip = 0 ;
			maxv = v = val ;
			maxidx = idx = id ;
		}

		inline void reverse () {
			if ( this == null ) return ;
			swap ( c[0] , c[1] ) ;
			flip ^= 1 ;
		}

		inline bool isroot () {
			return f == null || f->c[0] != this && f->c[1] != this ;
		}

		inline void up () {
			if ( this == null ) return ;
			maxv = v ;
			maxidx = idx ;
			if ( c[0]->maxv > maxv ) {
				maxv = c[0]->maxv ;
				maxidx = c[0]->maxidx ;
			}
			if ( c[1]->maxv > maxv ) {
				maxv = c[1]->maxv ;
				maxidx = c[1]->maxidx ;
			}
		}

		inline void down () {
			if ( this == null ) return ;
			if ( flip ) {
				c[0]->reverse () ;
				c[1]->reverse () ;
				flip = 0 ;
			}
		}

		inline void signdown () {
			if ( !isroot () ) f->signdown () ;
			down () ;
		}

		inline void setc ( Node* o , int d ) {
			c[d] = o ;
			o->f = this ;
		}

		inline void rot ( int d ) {
			Node* p = f ;
			Node* g = f->f ;
			p->setc ( c[d] , !d ) ;
			if ( !p->isroot () ) g->setc ( this , p == g->c[1] ) ;
			else f = g ;
			setc ( p , d ) ;
			p->up () ;
		}

		inline Node* splay () {
			signdown () ;
			while ( !isroot () ) {
				if ( f->isroot () ) 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 ) ;
					}
				}
			}
			up () ;
			return this ;
		}

		inline Node* access () {
			Node* o = this ;
			Node* x = null ;
			for ( ; o != null ; x = o , o = o->f ) {
				o->splay ()->setc ( x , 1 ) ;
				o->up () ;
			}
			return splay () ;
		}

		inline void makeroot () {
			access ()->reverse () ;
		}

		inline Node* findroot () {
			Node* o = access () ;
			for ( ; o->c[0] != null ; o = o->c[0] ) o->down () ;
			return o ;
		}

		inline void cut () {
			access () ;
			c[0] = c[0]->f = null ;
			up () ;
		}

		inline bool sameroot ( Node* o ) {
			return findroot () == o->findroot () ;
		}

		inline void link ( Node* o ) {
			//if ( this == o || sameroot ( o ) ) return ;
			makeroot () ;
			f = o ;
		}

		inline void cut ( Node* o ) {
			//if ( !sameroot ( o ) ) return ;
			makeroot () ;
			o->cut () ;
		}

		inline int ask ( Node* o ) {
			o->makeroot () ;
			return access ()->maxidx ;
		}
	} ;

	Node pool[MAXN + MAXE] ;
	Node* node[MAXN] ;
	Node* edge[MAXE] ;
	Node* cur ;

	void init () {
		cur = pool ;
		null = cur ++ ;
		null->newnode ( 0 , 0 ) ;
	}
} ;

struct Edge {
	int u , v , w ;
	bool operator < ( const Edge& a ) const {
		return w > a.w ;
	}
} ;

Edge E[MAXE] ;
int a[MAXE] ;
int n , m , q ;

int search1 ( int x , int l , int r ) {
	while ( l < r ) {
		int m = ( l + r ) >> 1 ;
		if ( a[m] >= x ) r = m ;
		else l = m + 1 ;
	}
	return l ;
}

int search2 ( int x , int l , int r ) {
	while ( l < r ) {
		int m = ( l + r + 1 ) >> 1 ;
		if ( a[m] <= x ) l = m ;
		else r = m - 1 ;
	}
	return l ;
}

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 l , r ;
	LCT :: init () ;
	PST :: init () ;
	scanf ( n ) ;
	scanf ( m ) ;
	PST :: build ( PST :: node[0] , 1 , m ) ;
	For ( i , 1 , m ) {
		scanf ( E[i].u ) ;
		scanf ( E[i].v ) ;
		scanf ( E[i].w ) ;
		a[i] = E[i].w ;
	}
	sort ( a + 1 , a + m + 1 ) ;
	sort ( E + 1 , E + m + 1 ) ;
	For ( i , 1 , n ) {
		LCT :: node[i] = LCT :: cur ++ ;
		LCT :: node[i]->newnode ( 0 , 0 ) ;
	}
	For ( i , 1 , m ) {
		LCT :: edge[i] = LCT :: cur ++ ;
		LCT :: edge[i]->newnode ( E[i].w , i ) ;
	}
	For ( i , 1 , m ) {
		int u = E[i].u ;
		int v = E[i].v ;
		PST :: insert ( PST :: node[i - 1] , PST :: node[i] , m - i + 1 , E[i].w , 1 , m ) ;
		if ( LCT :: node[u]->sameroot ( LCT :: node[v] ) ) {
			int idx = LCT :: node[u]->ask ( LCT :: node[v] ) ;
			LCT :: edge[idx]->cut ( LCT :: node[E[idx].u] ) ;
			LCT :: edge[idx]->cut ( LCT :: node[E[idx].v] ) ;
			PST :: insert ( PST :: node[i] , PST :: node[i] , m - idx + 1 , -E[idx].w , 1 , m ) ;
		}
		LCT :: edge[i]->link ( LCT :: node[u] ) ;
		LCT :: edge[i]->link ( LCT :: node[v] ) ;
	}
	int ans = 0 ;
	scanf ( "%d" , &q ) ;
	while ( q -- ) {
		scanf ( l ) ;
		scanf ( r ) ;
		l -= ans ;
		r -= ans ;
		l = m - search1 ( l , 1 , m ) + 1 ;
		r = search2 ( r , 1 , m ) ;
		ans = PST :: query ( PST :: node[l] , r , 1 , m ) ;
		printf ( "%d\n" , ans ) ;
	}
}

int main () {
	int T ;
	scanf ( T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值