zoukankan      html  css  js  c++  java
  • 【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
    ",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道题目!真是个喜庆的数字……

  • 相关阅读:
    快速排序
    冒泡排序
    桶排序
    Writer及其子类
    Reader及其子类
    OutputStream及其子类
    InputStream及其子类
    基础IO类和相关方法
    File类
    枚举
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793334.html
Copyright © 2011-2022 走看看