HDU 5741 Helter Skelter(二分)

Description
给出n个整数a[i]表示一个a[1]个0,a[2]个1,a[3]个0,…这样的字符串,给出m次询问,每次询问该字符串是否含有x个0和y个1的子串
Input
第一行为一整数T表示用例组数,每组用例首先输入两个整数n和m,之后n个整数a[i],最后m行每行两个整数x,y表示一次查询(1<=n<=1000,1<=m<=5*10^5,1<=a[i]<=10^6)
Output
对于每组用例,输出m个0或1表示该次查询结果
Sample Input
3
2 3
3 4
3 0
3 4
1 2
3 4
1 2 3
5 1
4 2
1 3
3 2
12 10
2 1 1 2 3 4 1 4 1 1 2 7
2 1
2 2
2 3
2 4
2 5
4 1
4 2
4 3
4 4
4 5
Sample Output
111
0101
1111101111
Solution
可以注意到对于一个固定的a, 可行的b一定是一个区间. 如果我们把所有可行的(a,b)画在二维平面上, 可以观察到一个有趣的现象: 这个可行区域一定是连通的, 且上下界有一些和x轴y轴平行的线段组成. 如下图所示.
显然, 求出这个上下边界这道题目就搞定了. 考虑求下边界, 观察上图可以知道, 求出所有红色的点就可以确定这个下边界. 同样, 所有绿色的点就可以确定上边界.
这里写图片描述
而简单分析可知所有红色的点都是以0开始,以0结束,所有绿色点都是以1开始以1结束,而且红色点和绿色点,对于相同横坐标,纵坐标非减,通过这个性质可以求出所有红色点和绿色点,对于每次查询(x,y),对红色点和绿色点分别二分搜索x即可确定y的上下界,判断y是否在上下界之间即可判断原串中是否含有x个0和y个1的子串
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 1111
struct node
{
    int x,y;
    node(){};
    node(int _x,int _y)
    {
        x=_x,y=_y;
    }
    bool operator <(const node &b)const
    {
        if(x!=b.x)return x<b.x;
        return y<b.y;
    }
}up[maxn*maxn],down[maxn*maxn];
int T,n,m,a[maxn],res1,res2;
char ans[555555];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        res1=res2=0;
        for(int i=0;i<n;i++)
        {
            int x=0,y=0;
            for(int j=i;j<n;j++)
            {
                if(j%2==0)x+=a[j];
                else y+=a[j];
                if(i%2==0&&j%2==0)down[res1++]=node(x,y);
                else if(i%2==1&&j%2==1)up[res2++]=node(x,y);
            }
        }
        sort(down,down+res1);
        int cnt=0;
        for(int i=0,j;i<res1;i=j)
        {
            for(j=i;j<res1&&down[j].x==down[i].x;j++);
            while(cnt>0&&down[cnt-1].y>=down[i].y)cnt--;
            down[cnt].x=down[i].x,down[cnt++].y=down[i].y;          
        }
        res1=cnt;
        sort(up,up+res2);
        cnt=0;
        for(int i=0,j;i<res2;i=j)
        {
            for(j=i;j<res2&&up[j].x==up[i].x;j++);
            if(!cnt||up[j-1].y>up[cnt-1].y)up[cnt].x=up[j-1].x,up[cnt++].y=up[j-1].y;
        }
        res2=cnt;
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int pos1=lower_bound(down,down+res1,node(a,-INF))-down;
            int pos2=lower_bound(up,up+res2,node(a,INF))-up;
            if(pos1<res1&&b>=down[pos1].y&&b<=up[pos2-1].y)ans[i]='1';
            else ans[i]='0';
        }
        ans[m]=0;
        printf("%s\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值