【CF575I】Robots protection(二维树状数组)
- 有一张\(n\times n\)的二维平面。
- \(q\)次操作,分为两种:摆放一个两直角边平行于坐标轴的等腰直角三角形;询问一个点被多少三角形覆盖。
- \(n\le5\times10^3,q\le10^5\)
二维树状数组
以斜边在左上方的直角三角形为例(可以通过每次将整个二维平面旋转\(90^\circ\)把四种直角三角形都化为这种情况)。
假设它的顶点坐标是\((x,y)\),边长是\(l\),那么被它覆盖的点\((X,Y)\)就需要满足:\(X+Y\le x+y+l\),\(X\ge x\),\(Y\ge y\)。
此时我们可以直接用树套树套树解决此题。
考虑我们修改一下条件,添上\(X+Y\ge x+y\)这个必要条件,则\(X\ge x\)和\(Y\ge y\)最多只有一个不满足。
因此我们容斥,给所有满足\(x+y\le X+Y\le x+y+l\)的点加\(1\),再给满足这个条件的前提下所有\(X<x\)和\(Y<y\)的点分别减\(1\)。
这样一来只需要一个树状数组和两个二维树状数组就解决了此题。
代码:\(O(qlog^2n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000
#define M 100000
using namespace std;
int n,Qt,ans[M+5];struct OP {int op,x,y,d,l;}q[M+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
struct TreeArray//树状数组
{
int a[2*N+5];I void Cl() {for(RI i=0;i<=2*n;++i) a[i]=0;}
I void U_(RI x,CI v) {W(x<=2*n) a[x]+=v,x+=x&-x;}I void U(CI x,CI y) {U_(x,1),U_(y+1,-1);}
I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}
}T;
struct TreeArray2//二维树状数组
{
int a[2*N+5][N+5];I void Cl() {for(RI i=0;i<=2*n;++i) for(RI j=0;j<=n;++j) a[i][j]=0;}
I void U1(int* a,RI x,RI v) {W(x) a[x]+=v,x-=x&-x;}
I void U2(RI x,CI y,CI v) {W(x<=2*n) U1(a[x],y,v),x+=x&-x;}
I void U(CI x1,CI x2,CI y) {U2(x1,y,1),U2(x2+1,y,-1);}
I int Q(int* a,RI x,RI t=0) {W(x<=n) t+=a[x],x+=x&-x;return t;}
I int Q(RI x,CI y,RI t=0) {W(x) t+=Q(a[x],y),x-=x&-x;return t;}
}X,Y;
int main()
{
RI i;for(read(n,Qt),i=1;i<=Qt;++i) read(q[i].op),
q[i].op==1?(read(q[i].d,q[i].x,q[i].y,q[i].l),q[i].d>2&&(q[i].d=7-q[i].d)):(read(q[i].x,q[i].y),0);//交换3,4类型,方便旋转
for(RI D=1,l,r;D<=4;++D) for(T.Cl(),X.Cl(),Y.Cl(),i=1;i<=Qt;swap(q[i].x,q[i].y),q[i].x=n-q[i].x+1,++i)//枚举类型,每次将所有点旋转90°
q[i].op==1&&q[i].d==D&&(T.U(l=q[i].x+q[i].y,r=q[i].x+q[i].y+q[i].l),X.U(l,r,q[i].x-1),Y.U(l,r,q[i].y-1),0),//放置这种类型的三角形
q[i].op==2&&(ans[i]+=T.Q(q[i].x+q[i].y)-X.Q(q[i].x+q[i].y,q[i].x)-Y.Q(q[i].x+q[i].y,q[i].y));//询问,用总情况减去两种不合法情况
for(i=1;i<=Qt;++i) q[i].op==2&&(writeln(ans[i]),0);return clear(),0;
}
待到再迷茫时回头望,所有脚印会发出光芒