JZOJ5006. A

28 篇文章 0 订阅
3 篇文章 0 订阅

题目大意

给出一个长度为 N ,宽度为1,高度无限的水箱,有N1个挡板将其分为 N 1×1×的小格,然后向每个小格中注水。
现在有 M 个条件,每个条件描述一个格子有水或者没水。
问最多能同时满足多少个条件。

Data Constraint
n,m100000

题解

如果我们能将这些区域的关系建成一棵树的话,就很好求答案了。
考虑扫描线,从下往上扫描,每次遇到一个挡板,就合并板两侧的区域,作为一个新区域,这个可以并查集维护。然后将条件挂到对应区域的点上即可。

时间复杂度: O(Nα(N))

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 5000000 + 10
struct Note {
    int w , h , type ;
} C[N] ;
struct Stype {
    int Num0 , Num1 ;
} P[N] ;

int Node[2*N] , Next[2*N] , Head[N] , tot ;
int f[N] , g[N] , fa[N] ;
int Case , n , m , Cnt ;

bool cmp( Note a , Note b ) { return a.h < b.h || ( a.h == b.h && a.type < b.type ) || ( a.h == b.h && a.type == b.type && a.w < b.w ) ; }

int Read() {
    int ret = 0 ;
    char ch = getchar() ;
    while ( ch < '0' || ch > '9' ) ch = getchar() ;
    while ( ch >= '0' && ch <= '9' ) {
        ret = ret * 10 + ch - '0' ;
        ch = getchar() ;
    }
    return ret ;
}

void link( int u , int v ) {
    Node[++tot] = v ;
    Next[tot] = Head[u] ;
    Head[u] = tot ;
}

int Get( int x ) { return fa[x] == x ? x : fa[x] = Get( fa[x] ) ; }

int NewNode() {
    ++ Cnt ;
    fa[Cnt] = Cnt ;
    P[Cnt].Num0 = P[Cnt].Num1 = 0 ;
    return Cnt ;
}

void Union( int x , int y ) {
    int fy = Get(y) ;
    if ( fy == x ) return ;
    link( x , fy ) ;
    fa[fy] = x ;
}

void DFS( int x ) {
    f[x] = P[x].Num1 ;
    g[x] = P[x].Num0 ;
    for (int p = Head[x] ; p ; p = Next[p] ) {
        DFS( Node[p] ) ;
        f[x] += f[Node[p]] ;
        g[x] += max( f[Node[p]] , g[Node[p]] ) ;
    }
}

int main() {
    Case = Read() ;
    while ( Case -- ) {
        tot = Cnt = 0 ;
        memset( P , 0 , sizeof(P) ) ;
        memset( Head , 0 , sizeof(Head) ) ;
        n = Read() , m = Read() ;
        for (int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
        for (int i = 1 ; i < n ; i ++ ) {
            C[m+i].w = i ;
            C[m+i].h = Read() ;
            C[m+i].h ++ ;
            C[m+i].type = -1 ;
        }
        for (int i = 1 ; i <= m ; i ++ ) {
            C[i].w = Read() ;
            C[i].h = Read() ;
            C[i].type = Read() ;
            C[i].h ++ ;
        }
        sort( C + 1 , C + n + m , cmp ) ;
        Cnt = n ;
        C[n+m].h = -1 ;
        for (int i = 1 ; i <= n + m - 1 ; i ++ ) {
            for (int j = i ; j <= n + m ; j ++ ) {
                if ( C[j].h != C[i].h ) {
                    i = j - 1 ;
                    break ;
                }
                if ( C[j].type == -1 ) {
                    int now = NewNode() ;
                    Union( now , C[j].w ) ;
                    Union( now , C[j].w + 1 ) ;
                } else {
                    int now = NewNode() ;
                    Union( now , C[j].w ) ;
                    if ( C[j].type == 0 ) P[now].Num0 ++ ;
                    else P[now].Num1 ++ ;
                }
            }
        }
        int Root = Get(1) ;
        DFS( Root ) ;
        printf( "%d\n" , max( f[Root] , g[Root] ) ) ;
    }
    return 0 ;
}

以上.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值