hdu5741

题目大意:给一个长度为n的序列用来表示一个只有01的字符串,a[1]表示开始有a[1]个0,a[2]表示有之后a[2]个1,a[3]表示之后又有a[3]个0...

有m个询问,每次询问给你一个ai和bi,问在原字符串中是否存在一个连续的区间,区间内的0的个数刚好为ai,1的个数刚好为bi,

有的话在最后输出答案的字符串的第i的字符是1,否则为0。

思路:要求的问题还是很清晰的,结论还是明显的,思路还是很好想的。

答案在一个连续的区间,这个连续的区间要么是在单个区间,即只有0或1的一个区间;

要么是刚好横跨了两个区间;要么是横跨了三个以上的区间,这个时候至少包含的1个或多个完整的区间;

于是我们对三种情况分别处理一下,对于每一种情况,我们可以设l为0的下界,r为0的上界,L为1的下界,R为1的上界(细节可以看代码)

然后对询问先按a排个序,如果ai刚好等于l,我们就可以把L-R这个区间更新到b的合法的区间中,如果ai刚好大于r,那在把L-R从b的合法的区间中拿出来

然后判断当前的bi是不是在合法的区间内,就好了。合法区间的更新具体实现可以用扫描线,或者树状数组水一水。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,m,a[1111];
char ans[maxn];//存答案最后用字符串输出,速度可能快点
int t[maxn*2];//树状数组
int A[maxn*2],Alen;//对不同的数离散化处理
struct Op
{
    int pos,d;
    int L,R;
    bool operator < (const Op &p) const
    {
        return pos<p.pos;
    }
}op[maxn*2],p;//存所有的操作
int oplen;
struct Q
{
    int id;
    int a,b;
    bool operator < (const Q &q) const
    {
        return a<q.a;
    }
}q[maxn];
int l,r;//0的数量的上下界
int L,R;//1的数量的上下界
void join()//根据l,r,L,R加入操作
{
    p.d=1,p.pos=l;
    p.L=L,p.R=R;
    op[oplen++]=p;

    p.d=-1,p.pos=r+1;
    op[oplen++]=p;

    A[++Alen]=L;
    A[++Alen]=R;
}
void add(int pos,int d)
{
    for(int i=pos;i<=Alen;i+=i&-i)
        t[i]+=d;
}
int sum(int pos)
{
    int ret=0;
    for(int i=pos;i>0;i-=i&-i)
        ret+=t[i];
    return ret;
}
int main()
{
    int i,j,k,T;
    scanf("%d",&T);
    while(T--)
    {
        Alen=oplen=0;
        memset(a,0,sizeof(a));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=1;i<=n;i++)
        {
            if(i&1)//单个区间内
                l=0,r=a[i],L=R=0;
            else 
                l=r=0,L=0,R=a[i];
            join();
            
            if(i<n)//i,i+1这两个相邻区间各取一部分

                if(i&1)
                    l=0,r=a[i],L=0,R=a[i+1];
                else
                    l=0,r=a[i+1],L=0,R=a[i];
                join(); 
            
            l=r=L=R=0;
            for(j=i;j<=n;j++)//至少包含从i-j之间的区间,然后向两边最多各延伸一个区间
            {
                if(j&1)
                {
                    l+=a[j];
                    r=l;
                    R=L+a[j+1];//向右延伸一个区间
                }
                else
                {
                    L+=a[j];
                    R=L;
                    r=l+a[j+1];
                }
                if(i&1)//向左延伸一个区间
                    R+=a[i-1];
                else
                    r+=a[i-1];
                join();
            }
        }
        for(i=1;i<=m;i++)
        {
            q[i].id=i;
            scanf("%d%d",&q[i].a,&q[i].b);
            A[++Alen]=q[i].b;
        }
        sort(q+1,q+m+1);
        sort(op,op+oplen);
        sort(A+1,A+Alen+1);
        Alen=unique(A+1,A+Alen+1)-A;
        for(i=0;i<=Alen;i++) t[i]=0;
        k=0;
        for(i=1;i<=m;i++)
        {
            for(;k<oplen && q[i].a>=op[k].pos;k++)
            {
                int ll=lower_bound(A+1,A+Alen,op[k].L)-A;
                int rr=lower_bound(A+1,A+Alen,op[k].R)-A;
                add(ll,op[k].d);
                add(rr+1,op[k].d*-1);
            }
            int pos=lower_bound(A+1,A+Alen,q[i].b)-A;
            ans[q[i].id-1]= (sum(pos)>0) + '0';
        }
        ans[m]=0;
        printf("%s\n",ans);
    }
    return 0;
}
/*
768
5 5
5 5 1 5 5
11 10
6 5
7 9
6 1
5 6


3 5
5 5 5
10 5
5 5
6 5
6 6
8 4


1 5
5
3 0
5 0
7 0
5 1
3 1


2 5
5 5
3 0
0 3
3 3
5 5
6 6
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值