[HDU5741] Helter Skelter [2016 Multi-University Training Contest 2(2016多校联合训练2) H]

题意

给定 01 序列,每次询问是否存在连续区间使得 0 的个数为ai, 1 的个数为bi.

题解

对于一个固定的 a ,可行的b是一个区间.如果我们把所有可行的 (a,b) 画在二维平面上,
H
可以观察到这个可行区域一定是连通的,且上下界有一些和 x y轴平行的线段组成.于是我们需要求出这个区域的上下边界.对于上边界中在线段转折处的点,一定是一个从 1 开始, 以1结尾的序列长度,下边界的线段转折点处的点,一定是一个从 0 开始, 以0结尾的序列长度.
枚举连续的以 0110 序列并排序,求下边界,枚举连续的 1001 序列求上边界,这就求出了可行区域的上下边界.
这样对每一个询问 (a,b) 我们二分求出与 a 对应的b的可行范围,就可以判断了.

代码

/****************************************\
* Author : ztx
* Title  : Helter Skelter
* ALG    :
* CMT    :
* Time   :
\****************************************/

#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}
template <typename TP>inline void readc(TP& ret) {
    while (ret=getchar() , ret<'!') ;
    while (CH=getchar() , CH>'!') ;
}
template <typename TP>inline void reads(TP *ret) {
    ret[0]=0;while (CH=getchar() , CH<'!') ;
    while (ret[++ret[0]]=CH,CH=getchar(),CH>'!') ;
    ret[ret[0]+1]=0;
}

#include <algorithm>

#define  maxn  1010LL
#define  maxt  1000010LL
#define  infi  1000000010LL

struct node {
    int a , b ;
    bool operator < (const node&B) const {
        if (a == B.a) return b < B.b ;
        return a < B.a ;
    }
} up[maxt] , down[maxt] , tmp ; int totd , totu ;

int a[maxn] , s0[maxn] , s1[maxn] ;

int main() {
int n , m , max0 , max1 , T , i , j , k , aa , bb ;
//  #define READ
    #ifdef  READ
        freopen(".in" ,"r",stdin ) ;
        freopen(".out","w",stdout) ;
    #endif
    for (read(T) ; T --> 0 ; ) {
        read(n) , read(m) ;
        s0[0] = s1[0] = a[0] = a[n+1] = max0 = max1 = 0 ;
        Rep (i,1,n) {
            read(a[i]) ;
            s0[i] = s0[i-1] , s1[i] = s1[i-1] ;
            if (i&1) s0[i] += a[i] , max0 = std::max(max0,a[i]) ;
            else s1[i] += a[i] , max1 = std::max(max1,a[i]);
        }
        totd = totu = 0 ;
        for (k = 1 ; k <= n ; k += 2)
            for (i = 1 ; j = i+k-1 , j <= n ; i += 2) {
                down[++totd] = (node){s0[j]-s0[i-1],s1[j]-s1[i-1]} ;
                up[++totu] = (node){s0[j]-s0[i-1],s1[j]-s1[i-1]+a[i-1]+a[j+1]} ;
            }
        up[++totu] = (node){0,max1} ;/// !!! !!! !!!
        std::sort(down+1,down+totd+1) ;
        std::sort(up+1,up+totu+1) ;
        /// Get Down find a lowest rising path
        k = totd ;
        for (totd = 1 , i = 2 ; i <= k ; i ++ )
            if (down[i].a != down[totd].a) {
                while (totd && down[totd].b >= down[i].b) totd -- ;
                down[++totd] = down[i] ;
            }
        down[++totd] = (node){infi,infi} ;
        /// Get Up find a highest rising path
        k = totu ;
        for (totu = 1 , i = 2 ; i <= k ; i ++ )
            if (up[i].a == up[totu].a) up[totu] = up[i] ;
            else if (up[i].b > up[totu].b) up[++totu] = up[i] ;
        up[++totu] = (node){infi,-1} ;
        /// Answer
        while (m -- ) {
            read(aa) , read(bb) ;
            if (!aa) { putchar((bb<=max1)+'0') ; continue ; }
            if (!bb) { putchar((aa<=max0)+'0') ; continue ; }
            if (aa > s0[n] || bb > s1[n]) { putchar('0') ; continue ; }
            i = std::upper_bound(down+1,down+totd+1,(node){aa,-1})-down ;
            if (down[i].b > bb) { putchar('0') ; continue ; }
            i = std::upper_bound(up+1,up+totu+1,(node){aa,infi})-up ;
            if (up[i-1].b < bb) { putchar('0') ; continue ; }
            putchar('1') ;
        }
        puts("") ;
    }
    #ifdef  READ
        fclose(stdin) ; fclose(stdout) ;
    #else
        getchar() ; getchar() ;
    #endif
    return 0 ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值