【BZOJ1062】糖果雨(NOI2008)-数形结合+二维树状数组

测试地址:糖果雨
做法:本题需要用到数形结合+二维树状数组。
这题看上去非常没有思路,因此我们来一步一步整理一下思路。
首先,我们要发现线段的颜色互不相同,并且移动的速度相等,这就说明它们的运动是周期性的,并且周期都是2len。原先我们要用一个三元组(t,l,r)来表示一条线段,而发现了上面的共性之后,我们就可以用一个二元组(time,length)来表示一条线段,具体来说,time是指在一个周期内,线段的左端点到达0的时刻,而length则是线段的长度。这样我们就可以将一条线段用一个二维平面上的点表示出来了。
接下来,考虑询问(t,l,r),首先将t2len取模,然后观察一条线段(time,length)与该询问相交的条件:trtimet+r,并且lengthl|timet|。我们可以在二维平面上画出一个奇怪的图形,而我们要求的就是这个图形内的点数。
然而这个奇怪的图形非常难算,我们可以把这个图形补成一些平行四边形,然而还是很难算,因此我们分两种情况,一种是往右下斜的平行四边形,一种是往右上斜的平行四边形,我们用两个扭曲的坐标系来把平行四边形扭成矩形计算,对于第一种平行四边形,进行变换(x,y)>(x,y+x)即可,而对于第二种平行四边形,进行变换(x,y)>(x,yx+2len)(为了保证坐标非负所以加了2len)即可。那么我们就可以用二维树状数组来维护单点修改,子矩阵查询了,时间复杂度为O(nlog2len)。具体的求子矩阵端点的式子可以看代码。
有一点小细节要注意,当r=len时,有一条直线上的点会被重复计算,要注意去重。还有,树状数组不能处理坐标为0的情况,将所有坐标+1即可。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,len,len2,len4;
int sum[2][4010][4010],x[1000010],y[1000010];

int lowbit(int x)
{
    return x&(-x);
}

int add(int type,int x,int y,int d)
{
    x++,y++;
    for(int i=x;i<=len2;i+=lowbit(i))
        for(int j=y;j<=len4;j+=lowbit(j))
            sum[type][i][j]+=d;
}

int Sum(int type,int x,int y)
{
    x++,y++;
    if (x<=0||y<=0) return 0;
    if (x>len2) x=len2;
    if (y>len4) y=len4;
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            ans+=sum[type][i][j];
    return ans;
}

int calc(int type,int x1,int y1,int x2,int y2)
{
    return Sum(type,x2,y2)-Sum(type,x2,y1-1)-Sum(type,x1-1,y2)+Sum(type,x1-1,y1-1);
}

int main()
{
    scanf("%d%d",&n,&len);
    len2=(len<<1),len4=(len<<2);
    for(int i=1;i<=n;i++)
    {
        int op,t,c,l,r,d,ans;
        scanf("%d",&op);
        if (op==1)
        {
            scanf("%d%d%d%d%d",&t,&c,&l,&r,&d);
            x[c]=(t-d*l+len2)%len2;
            y[c]=r-l;
            add(0,x[c],y[c]+x[c],1);
            add(1,x[c],y[c]-x[c]+len2,1);
        }
        if (op==2)
        {
            scanf("%d%d%d",&t,&l,&r);
            t%=len2;
            ans=0;
            int flag=(r==len);
            ans+=calc(0,t,l+t,t+r,len4);
            ans+=calc(0,0,l+t-len2,t+r-len2-flag,len4);
            ans+=calc(1,t-r+len2+flag,l-t,len2,len4);
            ans+=calc(1,t-r,l-t+len2,t-1,len4);
            printf("%d\n",ans);
        }
        if (op==3)
        {
            scanf("%d%d",&t,&c);
            add(0,x[c],y[c]+x[c],-1);
            add(1,x[c],y[c]-x[c]+len2,-1);
        }
    }

    return 0;
}

彩蛋:如果你看到了这里,告诉你一个小秘密——这是我在BZOJ上AC的第233道题目!真是个喜庆的数字……

posted @ 2018-06-13 11:39  Maxwei_wzj  阅读(204)  评论(0编辑  收藏  举报