p1247

 

看到n,m<=100000可以想到这是一个考高级数据结构的题,这个区间修改区间查询让我想到了线段树和树状数组。

然而看了一遍题后眉头一皱,发现并不简单。

它要求输出区间上炸弹种类数,那么维护区间和也不对,维护区间最大值也不对,看来不能直接套模板了。

这个时候需要重新综合考虑修改和查询的内容了。经过思考可以想到离散化的方法,把区间修改改成单点修改,区间查询即为求区间∑。

那么就可以写一个树状数组了, 毕竟代码好写很多很多很多。

对于每个修改把原数组左右端点所在位置++(注意开两个数组)。

对于每次询问嘛……

我们把1-n分为三部分:tl左,tl右tr左和tr右,记为123.那么所有炸弹有左1右1,左1右2,左1右3,左2右2,左2右3,左3右3.

我们查询的答案为左1右2+左1右3+左2右2+左2右3=sum-左1右1-左3右3.这也体现了数学里的正难则反的原则。

求法上还可以进一步优化:原式=sum-askyou(l-1)-(sum-sumzuo[r])=sum-zuo[r]-you[r-1];(直接给出这个式子你们应该也能理解)

本题可A。

int i,t,tl,tr;
int m,n;
int zuo[1000010],you[1000010];
int lowbit(int x)
{
    return x&(-x);
}
void addzuo(int p,int k)
{
    while(p<=n)
    {
        zuo[p]+=k;
        p+=lowbit(p);
    }
}
void addyou(int p,int k)
{
    while(p<=n)
    {
        you[p]+=k;
        p+=lowbit(p);
    }
}
int sumzuo(int x)
{
    int ans=0;
    while(x)
    {
        ans+=zuo[x];
        x-=lowbit(x);
    }
    return ans;
}
int sumyou(int x)
{
    int ans=0;
    while(x)
    {
        ans+=you[x];
        x-=lowbit(x);
    }
    return ans;
}
int main()
{

    cin>>n>>m;
    
    for(i=1;i<=m;i++)
    {
        cin>>t>>tl>>tr;
        if(t==1)
        {
            addzuo(tl,1);
            addyou(tr,1);
        }
        else
            
            cout<<sumzuo(tr)-sumyou(tl-1)<<endl;
    }
}

还好没有卡tl=1或等0的数据,不然就死循环…………

posted @ 2018-09-10 21:34  zzuqy  阅读(144)  评论(0编辑  收藏  举报