Description
Solution
如图,假如我们知道了以任何一个点为顶点的135-180度的前缀和和90-180度的前缀和,我们就可以搞出三角形的面积。
差分。add[i][j]和dev[i][j]都表示相对点[i][j-1],点[i][j]应该+或-的大小。这样只要我们需要,可以在O(n2)的时间里求出整个图的前缀和。
然后,不可能每一次查询都求一次前缀和的。考虑分块。记录当前添加的修改的操作数cnt。如果cnt=2500,则把图的前缀和全部求出来,对cnt,add,dev初始化。
假如中途有询问,就计算好之前分出的若干块对本次询问的贡献后,i直接从1到cnt枚举,判断当前的修改对询问的贡献。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long ll; int n,Q;int opt,x,y,a,cnt,xx[2510],yy[2510],aa[2510]; ll sum_slain[1010][1010],sum_line[1010][1010],num[1010][1010]; int add[1010][1010],dev[1010][1010],cur[1010][1010]; void work() { for (int i=1;i<=n;i++) for (int j=1;j<=i;j++) { add[i][j]+=add[i-1][j]; dev[i][j]+=dev[i-1][j-1]; cur[i][j]=cur[i][j-1]+add[i][j]-dev[i][j]; num[i][j]+=cur[i][j]; sum_line[i][j]=sum_line[i-1][j]+sum_line[i][j-1]-sum_line[i-1][j-1]+num[i][j]; sum_slain[i][j]=sum_slain[i-1][j-1]+sum_line[i][j-1]-sum_line[i-1][j-1]+num[i][j]; } memset(add,0,sizeof(add));memset(dev,0,sizeof(dev));cnt=0; } ll ans; int main() { scanf("%d%d",&n,&Q); while (Q--) { scanf("%d%d%d%d",&opt,&x,&y,&a); if (opt==1) { cnt++; add[x][y]++;add[x+a][y]--; dev[x][y+1]++;dev[x+a][y+a+1]--; xx[cnt]=x;yy[cnt]=y;aa[cnt]=a; if (cnt==2500) work(); } else { ans=sum_slain[x+a-1][y+a-1]-sum_slain[x-1][y-1]-sum_line[x+a-1][y-1]+sum_line[x-1][y-1]; for (int i=1;i<=cnt;i++) { int X=min(xx[i]+aa[i]-1,x+a-1); int Y=max(yy[i],y); int Z=max(xx[i]-yy[i],x-y); int len=X-Z-Y+1; if (len>0) ans+=1ll*len*(len+1)/2; } printf("%lld ",ans); } } }