[HNOI2009]积木游戏 题解

Tag : 三元环+欧拉公式

先用线段树求出每个方块的位置。
然后将有公共点的方块连边。
发现每个空腔对应图上的一个大于三元的环面。
然后根据欧拉公式我们知道环面数等于边数+2-顶点数。求出所有的环面,然后统计三元环的个数减去。
然后加上被多减去的交叉三元环的贡献。
时间复杂度 O( n n n\sqrt{n} nn ),细节比较多,代码巨长。

代码:

#include<bits/stdc++.h>
using namespace std;

int n, flag; 

int max( int a, int b, int c )
  { return max( a, max( b, c ) ); }

int max( int a, int b, int c, int d )
  { return max( a, max( b, c, d ) ); }

bool cross( int a, int b, int x, int y )
  { return max( a, x ) <= min(b, y); } 

namespace SGT{
    #define ls ( x << 1 )
    #define rs ( ls | 1 )
    #define mid ( ( l[x] + r[x] ) >> 1 )
    #define Lrange ls, L, min( mid, R )
    #define Rrange rs, max( mid + 1, L ), R
    #define Ason(F) ( (F) ? ls : rs )
    int l[100005 << 2];
    int r[100005 << 2];
    int mx[100005 << 2];
    int mk[100005 << 2];
    
    void push_up( int x ) { mx[x] = max( mx[ls], mx[rs] ); }
    
    void push_down( int x )
      { mk[ls] = max( mk[ls], mk[x]);
        mk[rs] = max( mk[rs], mk[x]);
        if( mx[ls] < mk[ls] ) mx[ls] = mk[ls];
        if( mx[rs] < mk[rs] ) mx[rs] = mk[rs]; }

    void build( int x, int L, int R ) 
      { l[x] = L, r[x] = R;
        if( L == R ) return ;
        build( Lrange ); build( Rrange ); }
    
    void modify( int x, int L, int R, int V ) 
      { if( l[x] == L and r[x] == R ) return mk[x] = max( mk[x], V ), mx[x] = max( V, mx[x] ), void(); push_down(x);
        if( L <= mid ) modify( Lrange, V );
        if( R >  mid ) modify( Rrange, V );
        push_up(x); }
    
    int query( int x, int L, int R ) 
      { if( l[x] == L and r[x] == R ) return mx[x];
        push_down(x);
        if( R <= mid ) return query( Lrange );
        if( L >  mid ) return query( Rrange );
        return max( query( Lrange ), query( Rrange ) ); }
}

int cnt[100005];

namespace GRP{
	vector<int> to1[100005]; 
	vector<int> to2[100005];
	int deg[100005];
	int tag[100005];
	map< pair<int, int>, bool > MP;
	
	void addEdge( int u, int v )
	  { if( MP[ make_pair( u, v ) ] or MP[ make_pair( v, u ) ] ) return ;
	    MP[ make_pair( u, v ) ] = 1;
	    to1[u].push_back(v); deg[u] ++;
	    to1[v].push_back(u); deg[v] ++; }
	
	void countTriangle() 
	  { for( int i = 0; i <= n; i ++ ) for( int j : to1[i] )
	      { if( deg[i] > deg[j] or ( deg[i] == deg[j] and i > j ) ) 
		      { to2[i].push_back(j); }
		  }
		  
		for( int i = 0; i <= n; i ++ )
		  { for( int j : to2[i] ) tag[j] = i + 1;
		    for( int j : to2[i] ) for( int k : to2[j] ) if( tag[k] == i + 1 ) 
			  { cnt[ max( i, j, k ) ] --; } 
		  }
	  }
	  
	void flush( int x )
	  { for( int i : to1[x] ) if( i < x ) cnt[x] ++; cnt[x] --; }
}

struct Rect
  { int l, r, b, t, h, id;
    void get( int _id )
      { id = _id; cin >> l >> r >> h; }
  } R[100005], T[100005];

bool cmp1( Rect& A, Rect& B ) { return A.l == B.l ? A.b < B.b : A.l < B.l; }
bool cmp2( Rect& A, Rect& B ) { return A.r == B.r ? A.b < B.b : A.r < B.r; }
bool cmp3( Rect& A, Rect& B ) { return A.b == B.b ? A.l < B.l : A.b < B.b; }
bool cmp4( Rect& A, Rect& B ) { return A.t == B.t ? A.l < B.l : A.t < B.t; }

struct Point
  { int x, y, id;
  	Point( int _x, int _y, int _id )
  	  : x(_x), y(_y), id(_id) { }
  	bool operator <( const Point& F )
	  { return x == F.x ? y < F.y : x < F.x; } 
	bool operator ==( const Point& F )
	  { return x == F.x and y == F.y; }
  } ;

vector<Point> VP; 

int main(){
    cin >> n;

    SGT::build( 1, 1, 100000 );	
	
    for( int i = 1; i <= n; i ++ ) R[i].get(i);
    for( int i = 1; i <= n; i ++ ) 
	  { int Q = SGT::query( 1, R[i].l + 1, R[i].r );
        R[i].b = Q; R[i].t = R[i].h + Q;
        SGT::modify( 1, R[i].l + 1, R[i].r, R[i].t ); } 
	
	for( int i = 1; i <= n; i ++ ) if( R[i].b == 0 )
	  { GRP::addEdge( 0, i ); }
	
	for( int i = 1; i <= n; i ++ ) T[i] = R[i];
	
//#define BF
#ifndef BF
	for( int i = 1; i <= n; i ++ ) T[i] = R[i];
	
	sort( R + 1, R + n + 1, cmp1 );
	sort( T + 1, T + n + 1, cmp2 );
	
	for( int l = 1, r = 1, p; l <= n; l ++ )
	  { while( T[r].r < R[l].l or ( T[r].r == R[l].l and T[r].t < R[l].b ) ) r ++; p = r;
		while( T[p].r == R[l].l and T[p].b <= R[l].t ) GRP::addEdge( R[l].id, T[p].id ), p ++;	}
		
	sort( R + 1, R + n + 1, cmp3 );
	sort( T + 1, T + n + 1, cmp4 );
	
	for( int l = 1, r = 1, p; l <= n; l ++ )
	  { while( T[r].t < R[l].b or ( T[r].t == R[l].b and T[r].r < R[l].l ) ) r ++; p = r;
		while( T[p].t == R[l].b and T[p].l <= R[l].r ) GRP::addEdge( R[l].id, T[p].id ), p ++;	}
	
#else
	for( int i = 1; i <= n; i ++ ) for( int j = 1; j <= n; j ++ )
	  { if( R[i].b == R[j].t and cross( R[i].l, R[i].r, R[j].l, R[j].r ) ) 
	      { GRP::addEdge( i, j ); }
	    if( R[i].l == R[j].r and cross( R[i].b, R[i].t, R[j].b, R[j].t ) ) 
	      { GRP::addEdge( i, j ); }
	  }
#endif 
	
	GRP::countTriangle();
 
	for( int i = 1; i <= n; i ++ )
	  { VP.push_back( Point( R[i].l, R[i].b, R[i].id ) );
	    VP.push_back( Point( R[i].l, R[i].t, R[i].id ) ); 
		VP.push_back( Point( R[i].r, R[i].b, R[i].id ) );
		VP.push_back( Point( R[i].r, R[i].t, R[i].id ) ); }
	
	sort( VP.begin(), VP.end() );
	
	for( int i = 0; i < (n - 1) * 4; i ++ )
	  { if( VP[i] == VP[i + 1] ) if( VP[i] == VP[i + 2] ) if( VP[i] == VP[i + 3] )
	      { cnt[ max( VP[i].id, VP[i + 1].id, VP[i + 2].id, VP[i + 3].id ) ] ++; } } 

	
	for( int i = 1; i <= n ; i ++ ) GRP::flush( i );
	for( int i = 1; i <= n ; i ++ ) cout << cnt[i] << "\n";
	
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值