线段树(先占个坑,有空去填)

uva11983 - Weird Advertisement(扫描线)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
using namespace std ;

const int maxn = 611111 ;
ll sum[15][maxn<<2] ;
int cnt[maxn<<2] , f[maxn<<1] , n , k ;

struct Edge{
	int l , r , h , id ;
	bool operator < ( const Edge &p ) const{
		return h < p.h ;
	}
} edge[maxn<<1] ;

void build(int l,int r,int rt){
    cnt[rt]=0;
    sum[0][rt]=f[r+1]-f[l];
    for(int i=1;i<=k;i++)sum[i][rt]=0;
    if(l==r)return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}

void push_up(int l,int r,int rt)//将当前的值都清空,然后重新计算
{
	int i ;
	for ( i = 0 ; i <= k ; i ++ ) sum[i][rt] = 0 ;
	if ( cnt[rt] >= k ) sum[k][rt] = f[r+1] - f[l] ;
	else if ( l == r ) sum[cnt[rt]][rt] = f[r+1] - f[l] ;
	else{
		for ( i = k - cnt[rt] ; i <= k ; i ++ ) sum[k][rt] += sum[i][rt<<1] + sum[i][rt<<1|1] ;
		for ( i = cnt[rt] ; i < k ; i ++ ) sum[i][rt] = sum[i-cnt[rt]][rt<<1] + sum[i-cnt[rt]][rt<<1|1] ;
	}
}

void update(int L,int R,int flag,int l,int r,int rt){
    if(L<=l&&R>=r){
        cnt[rt]+=flag;
        push_up(l , r , rt);
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)update(L,R,flag,lson);
    if(R>m)update(L,R,flag,rson);
    push_up(l,r,rt);
}

int main () {
	int a , b , c , d , cas , i , j , ca = 0 ;
	scanf ( "%d" , &cas ) ;
	while ( cas -- ){
		scanf ( "%d%d" , &n , &k ) ;
		int tot = 0 ;
		ll ans = 0 ;
		for ( i = 1 ; i <= n ; i ++ ){
			scanf ( "%d%d%d%d" , &a , &b , &c , &d ) ;
			c ++ , d ++ ;
			tot ++ ;
			edge[tot].l = a , edge[tot].r = c , edge[tot].h = b , edge[tot].id = 1 ;
			f[tot] = a ;
			tot ++ ;
			edge[tot].l = a , edge[tot].r = c , edge[tot].h = d , edge[tot].id = -1 ;
			f[tot] = c ;
		}
		sort ( f + 1 , f + tot + 1 ) ;
		sort ( edge + 1 , edge + tot + 1 ) ;
		int T = unique ( f + 1 , f + tot + 1 ) - f - 1 ;
		build ( 1 , T - 1 , 1 ) ;
		for ( i = 1 ; i < tot ; i ++ ){
			int l = lower_bound ( f + 1 , f + T + 1 , edge[i].l ) - f ;
			int r = lower_bound ( f + 1 , f + T + 1 , edge[i].r ) - f - 1 ;
			update ( l , r , edge[i].id , 1 , T - 1 , 1 ) ;
			ans += sum[k][1] * (ll) ( edge[i+1].h - edge[i].h ) ;
		}
		printf ( "Case %d: %lld\n" , ++ ca , ans ) ;
	}
}
题意:有一些分散的城市,修建一些边将这些城市连接起来,在同一个连通块的城市被称为一个州。有两种操作,(1)修建一条road,将城市a,b连接起来。(2)询问纵坐标为c的一条水平线能穿过几个州,穿过的这些州的城市总共和是多少。
解题思路:并差集+线段树,并差集里面保存的是州的信息,要记录的有州的根节点,州的纵坐标的最大值和最小值,以及这个州的size(也就是这个州包含了几个城市,横坐标其实没有用)。建两棵线段树,分别保存[l,r]有多少个州,[l,r]有多少个城市,那么询问时就是单点询问了(c告诉你是k*0.5的,你可以把所有的数都扩大两倍,那么就全都是整数了)。road操作时,找到这两个城市所属的集合,先看给你的两个城市是否在同一个集合,如果在同一个集合,那就不进行任何操作。否则就根据这两个集合纵坐标的最值关系进行区间更新(这个要分类讨论,城市的个数是不会少的,但州的个数有可能会减,仔细讨论清楚)。然后将两个城市归并到同一个集合。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
using namespace std ;

const int maxn = 1000002 ;

int sum[2][maxn<<2] , add[2][maxn<<2] ;

struct FA{
	int fa , u , d , size ;
} f[maxn] ;

int find ( int a ) { return f[a].fa = ( a == f[a].fa ? a : find ( f[a].fa ) ) ; }
int p[maxn] ;

void push_down ( int rt , int flag ){
	if ( add[flag][rt] ){
		add[flag][rt<<1] += add[flag][rt] , add[flag][rt<<1|1] += add[flag][rt] ;
		sum[flag][rt<<1] += add[flag][rt] , sum[flag][rt<<1|1] += add[flag][rt] ;
		add[flag][rt] = 0 ;
	}
}

void update ( int a , int b , int flag , int c , int l , int r , int rt ) {
	if ( a > b ) return ;
	if  ( a <= l && r <= b ){
//		printf ( "sum[%d][%d]" ) ;
		sum[flag][rt] += c ;
//    	printf ( "a = %d , b = %d , l = %d , r = %d , c = %d\n" , a , b , l , r , c ) ;
		add[flag][rt] += c ;
		return ;
	}
	push_down ( rt , flag ) ;
	int m = ( l + r ) >> 1 ;
	if ( a <= m ) update ( a , b , flag , c , lson ) ;
	if ( m < b ) update ( a , b , flag , c , rson ) ;
	sum[flag][rt] = sum[flag][rt<<1|1] + sum[flag][rt<<1] ;
}

int query ( int a , int flag , int l , int r , int rt ){
	if ( l == r ) return sum[flag][rt] ;
	push_down ( rt , flag ) ;
	int m = ( l + r ) >> 1 , ret = 0 ;
	if ( a <= m ) ret = query ( a , flag , lson ) ;
	else ret = query ( a , flag , rson ) ;
	return ret ;
}

char s[111] ;

int main (){
	int n , i , j , k , cas , a , b , m , l , r , x  , y ;
	double c ;
	scanf ( "%d" , &cas ) ;
	while ( cas -- ){
		scanf ( "%d" , &n ) ;
		for ( i = 0 ; i < n ; i ++ ) {
			scanf ( "%d%d", &a , &b ) ;
			f[i].u = f[i].d = p[i] = b * 2 ;
			f[i].size = 1 , f[i].fa = i ;
			update ( b , b , 0 , 1 , 0 , maxn - 111 , 1 ) ;
			update ( b , b , 1 , 1 , 0 , maxn - 111 , 1 ) ;
		}
		memset ( sum , 0 , sizeof ( sum ) ) ;
		memset ( add , 0 , sizeof ( add ) ) ;
		scanf ( "%d" , &m ) ;
		while ( m -- ){
			scanf ( "%s" , s ) ;
			if ( s[0] == 'l' ){
				scanf ( "%lf" , &c ) ;
				c *= 2 ;
				printf ( "%d %d\n" , query ( c , 1 , 0 , maxn - 111 , 1 ) , query ( c , 0 , 0 , maxn - 111 , 1 ) ) ;
			}
			else{
				scanf ( "%d%d" , &l , &r ) ;
				x = find ( l ) , y = find ( r ) ;
				if ( x == y ) continue ;
				int u1 = f[x].u , u2 = f[y].u , d1 = f[x].d , d2 = f[y].d ;
	//			printf ( "%d %d %d %d %d %d\n" , x , y , u1 , u2 , d1 , d2 ) ;
				if ( d1 > u2 ) {
					update ( u2 + 1 , u1 , 0 , f[y].size , 0 , maxn - 111 , 1 ) ;
					update ( d2 , d1 - 1 , 0 , f[x].size, 0 , maxn - 111 , 1 ) ;
					if ( d1 > u2 + 1 ) update ( u2 + 1 , d1 - 1 , 1 , 1 , 0 , maxn - 111 , 1 ) ;
				}
				else if ( d2 > u1 ){
					update ( u1 + 1 , u2 , 0 , f[x].size , 0 , maxn - 111 , 1 ) ;
					update ( d1 , d2 - 1 , 0 , f[y].size, 0 , maxn - 111 , 1 ) ;
					if ( d2 > u1 + 1 ) update ( u1 + 1 , d2 - 1 , 1 , 1 , 0 , maxn - 111 , 1 ) ;
				}
				else if ( u2 >= d1 && u2 <= u1 ) {
					update ( u2 + 1 , u1 , 0 , f[y].size , 0 , maxn - 111 , 1 ) ;
					if ( d2 < d1 ) update ( d2 , d1 - 1 , 0 , f[x].size, 0 , maxn - 111 , 1 ) ;
					else update ( d1 , d2 - 1 , 0 , f[y].size , 0 , maxn - 111 , 1 ) ;
//					puts ( "fuck" ) ;
					update ( max ( d1 , d2 ) , u2 , 1 , -1 , 0 , maxn - 111 , 1  ) ;
				}
				else {
					update ( u1 + 1 , u2 , 0 , f[x].size , 0 , maxn - 111 , 1 ) ;
					if ( d1 < d2 ) update ( d1 , d2 - 1 , 0 , f[y].size, 0 , maxn - 111 , 1 ) ;
					else update ( d2 , d1 - 1 , 0 , f[x].size , 0 , maxn - 111 , 1 ) ;
					update ( max ( d1 , d2 ) , u1 , 1 , -1 , 0 , maxn - 111 , 1  ) ;
				}
				f[y].u = max ( f[y].u , f[x].u ) ;
				f[y].d = min  ( f[y].d , f[x].d ) ;
				f[x].fa = y ;
				f[y].size += f[x].size ;
	//			printf ( "%d %d\n" , query ( 9 , 1 , 0 , maxn - 111 , 1 ) , query ( 9 , 0 , 0 , maxn - 111 , 1 ) ) ;
			}
		}
	}
	return 0 ;
}
/*
3 
10 
1 7 
5 7 
8 6 
3 5 
5 5 
2 3 
10 3 
7 2 
4 1 
11 1 
11 
road 0 1 
road 3 5 
road 4 2 
road 3 8
line 4.5
*/



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值