HDU 5741 Helter Skelter(构造法)

forever97 posted @ 2016年7月25日 10:59 in 数学-构造法 with tags 二分 构造法 , 582 阅读

 

【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=5741

 

【题目大意】

    一个01相间的串,以0开头,给出的序列每个数字表示连续的0的个数或者1的个数,现在有m个询问,求0的个数为a且1的个数为b的串是否存在。

 

【题解】

    我们发现形如11001这样子以1为开头结尾的串是包含1001这样子的串的,同理以0为开头结尾的串也是包含了一些开头结尾数字相同的子串。

    可以发现,当0的个数固定,1的个数是数轴上的一个区间,而且在0的个数相差1时,必定可以取到相同的1的个数,因此可行域在二维平面内是一个实心的联通图,且上边界和下边界的点坐标单调非减。

    那么我们首先计算出上下边界的点,可以发现在0的个数固定的情况下,1的个数的上界一定是由1开始,1结尾的子串产生的,下界是由0开始,0结尾的子串产生的,那么保存这些点。

    然而在图形中两个横纵坐标都不相同的点就能够确定一个矩形可行区域,因此,只要保留上下边界坐标均单调递增的点即可,二分查询(a,b)是否在连通块区域内,就能够判断是否存在这样子的子串

 

【代码】

#include <cstdio>
#include <utility>
#include <climits>
#include <algorithm>
using namespace std;
const int N=1010,M=N*N;
typedef pair<int,int> PII;
char ans[M];
int T,n,m,a,b,t[N],cntd,cntu,cnt;
PII D[M],U[M];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)scanf("%d",&t[i]);
        cntd=cntu=cnt=0;
        for(int i=0;i<n;i++){
            int x=0,y=0;
            for(int j=i;j<n;j++){
                if(j%2)y+=t[j];else x+=t[j];
                if(i%2==0&&j%2==0)D[cntd++]=PII(x,y);
                if(i%2&&j%2)U[cntu++]=PII(x,y);
            }
        }sort(D,D+cntd);
        for(int i=0,j;i<cntd;i=j){
            for(j=i;j<cntd&&D[i].first==D[j].first;j++);
            while(cnt&&D[cnt-1].second>=D[i].second)cnt--;
            D[cnt++]=D[i]; 
        }cntd=cnt;
        sort(U,U+cntu); cnt=0;
        for(int i=0,j;i<cntu;i=j){
            for(j=i;j<cntu&&U[i].first==U[j].first;j++);
            if(!n||U[cnt-1].second<U[j-1].second)U[cnt++]=U[j-1];
        }cntu=cnt;
        for(int i=0;i<m;i++){
            scanf("%d%d",&a,&b);
            int x=upper_bound(U,U+cntu,PII(a,INT_MAX))-U;
            int y=upper_bound(D,D+cntd,PII(a,INT_MIN))-D;
            ans[i]='0'+(y<cntd&&U[x-1].second>=b&&D[y].second<=b);
        }ans[m]=0; puts(ans);
    }return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter