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