题意:
二维平面的第一象限内,有两种操作,一是在某个点的权值加一,二是查询一个直角梯形范围内的权值的和。
题解:
std解法是CDQ分治,想了个分块暴力的方法,速度竟然是最快的。。。
将整个第一象限分为128*128(1<<7)个块,每个块是边长为(1<<23)的正方形,这样可以表示坐标(1<<30)范围内的点,满足题目给出的范围
建立一个128*128的二维树状数组,用来求一个矩形区域内的权值的和
对于第一种操作,求出点所在的块,在二维树状数组中单点修改就行
关键是第二种操作
一个直角梯形:
分解为:
矩形和直角三角形分开计算
矩形部分:
先确定矩形的边界占据的块,即求出矩形所在块的x方向和y方向的范围:l , r , dn , up
那么,范围 l+1 ~ r-1 , dn+1 ~ up -1 内的块中的点一定是在矩形范围内的,就是下图中的绿色部分
绿色部分的权值和用二维树状数组解决,4条边界所经过的块就必须要遍历一遍,对于每一个块,看块中包含的点在矩形内的个数,两部分加一起就是矩形范围内的权值和
然后是三角形部分
由于不是矩形,无法通过二维树状数组一次求解,方法是遍历左右区间,即 x 方向在 [ l+1 , r-1 ] ,y方向为 dn+1 的块,就是绿色块的底边,通过直线方程可以计算出当前列的绿色块的上限,显然每一列的个数是不同的,这样一列一列计算出绿色块包含的权值和(这里由于是一列,所以可以用一维树状数组求解),再遍历边界经过的块,两部分加一起就是三角形范围内的权值和
具体实现起来方法很多,不同方法需要考虑的细节不一样,下面的方法需要考虑的细节比较少。
复杂度分析
最坏1e5个点,1e4多个块,由于数据随机,平均一个块不到10个点
操作一复杂度为二维树状数组复杂度:log*log,即7*7
操作二复杂度
矩形:一个二维树状数组查询:log*log,边界经过的块的期望值为128个,每个块平均有10个点,总复杂度为128*10
三角形:边界经过的块的期望值为128个,求绿色块时每一列查询一次一维树状数组,单次为log,共128*7,边界复杂度同矩形,为128*10,总复杂度为128*10
所以,操作二复杂度为128*10
由于操作一二是随机出现,实际复杂度远远达不到上限,原因:首先前期每个块中点的个数远达不到10,后期个数可能会多一点,但这一定是操作一过多导致的,这又导致操作二变得很少,于是发现真正耗时间的操作二少很多。一次操作姑且期望为 1e3,共有1e5个操作,总用时1e8
代码:
#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define mod 998244353
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
struct Point
{
int x,y;
};
vector<Point> a[1<<7][1<<7];
int dd[1<<7][1<<7],f[1<<7][1<<7],op,x,y,xx,d;
inline void update(int x,int y)
{
for (int i=x;i<128;i|=(i+1))
for (int j=y;j<128;j|=(j+1))
dd[i][j]++;
for (int i=y;i<128;i|=(i+1))
f[x][i]++;
}
inline int sum(int x,int y)
{
int res=0;
for (int i=x;i>=0;i=(i&(i+1))-1)
for (int j=y;j>=0;j=(j&(j+1))-1)
res+=dd[i][j];
return res;
}
inline int gsum(int x,int y)
{
int res=0;
for (int i=y;i>=0;i=(i&(i+1))-1)
res+=f[x][i];
return res;
}
inline int spyer(int l,int r,int dn,int up) //矩形
{
if (r>127) r=127;
if (up>127) up=127;
int res=0,t=xx+d+y;
for (int i=l;i<=r;i++)
{
for (int j=0;j<a[i][up].size();j++) //上边界
{
int tx=a[i][up][j].x,ty=a[i][up][j].y;
if (tx>=x && tx<=xx && ty<=y+d && ty>=y) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
if (dn<up)
for (int j=0;j<a[i][dn].size();j++) //下边界
{
int tx=a[i][dn][j].x,ty=a[i][dn][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
}
for (int i=dn+1;i<up;i++)
{
for (int j=0;j<a[l][i].size();j++) //左边界
{
int tx=a[l][i][j].x,ty=a[l][i][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
if (l<r)
for (int j=0;j<a[r][i].size();j++) //右边界
{
int tx=a[r][i][j].x,ty=a[r][i][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
}
l++;r--;dn++;up--;
if (l>r || dn>up) return res;else
return res+sum(r,up)-sum(l-1,up)-sum(r,dn-1)+sum(l-1,dn-1);
}
inline int spyerr() // 三角形
{
int res=0;
int nx=((xx>>23)+1)<<23,t=xx+d+y; //三角形的左边界在矩形上其实已经算过了,重新确定需要计算的左边界,一定是某一个块的起始位置
xx+=d;
if (nx>xx) return 0;
if ((nx>>23)>127) return 0;
int l=nx>>23,r=xx>>23,dn=y>>23;
for (int i=l;i<=r;i++) // 计算下边界
{
for (int j=0;j<a[i][dn].size();j++)
{
int tx=a[i][dn][j].x,ty=a[i][dn][j].y;
if (tx>=nx && tx<=xx && ty>=y && tx+ty<=t) res++;
}
}
int ny=(dn+1)<<23; y=t-nx; //重新确定需要计算的下边界,一定是某一个块的起始位置
xx=t-ny; r=xx>>23;
if (ny>y) return res;
for (int i=l;i<=r;i++)
{
int tx=i<<23,ttx=((i+1)<<23)-1,ty=t-tx,tty=t-ttx,t1=ty>>23,t2=tty>>23; //当前列的块的坐标左右边界所处的块为t1,t2
if (t1<128) for (int j=0;j<a[i][t1].size();j++) // 斜边经过的块的计算
{
int tx=a[i][t1][j].x,ty=a[i][t1][j].y;
if (tx>=nx && tx<=xx && ty>=ny && tx+ty<=t) res++;
}
if (t2!=t1 && t2>dn && t2<128)
for (int j=0;j<a[i][t2].size();j++) // 斜边经过的块的计算
{
int tx=a[i][t2][j].x,ty=a[i][t2][j].y;
if (tx>=nx && tx<=xx && ty>=ny && tx+ty<=t) res++;
}
if (t2>127) t2=128;
if (t2-1>dn)res+=gsum(i,t2-1)-gsum(i,dn); //绿色的块计算
}
return res;
}
int main()
{
int n;
sc(n);
for (int i=1;i<=n;i++)
{
sc(op);
if (op==1)
{
scc(x,y);
a[x>>23][y>>23].pb(Point{x,y});
update(x>>23,y>>23);
}else
{
scc(x,y); scc(xx,d);
int tmp=spyer(x>>23,xx>>23,y>>23,(y+d)>>23);
tmp+=spyerr();
printf("%d\n",tmp);
}
}
}