题意
一个螺旋的矩阵,大小为n*n,n一定为奇数。有m个位置有值,值就是螺旋矩阵上的值,其他没有列举的位置都为0. 给了x1,y1,x2,y2,求矩形区域(x1,y1)(x2,y2)内所有数的综合。
思路
求矩形区域,可以转化为求四个前缀和。但是1e6限制了不能用二维树状数组,所以求前缀和的时候按照Y从小到大的顺序,将二维转化为一维。
会爆int,用ll存
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll getNum(ll x,ll y,ll n) //得到螺旋矩阵的数
{
x = x-n/2-1;
y = y-n/2-1;
ll t = max(abs(x),abs(y));//第几个螺旋
ll ans;
if(x>=y) ans = n*n-4*t*t-2*t-x-y;
else ans = n*n-4*t*t+2*t+x+y;
return ans;
}
ll getDigit(ll x) //求数的各位数字和
{
ll ans = 0;
while(x){
ans += x%10;
x /=10;
}
return ans;
}
const int N = 1e6+10;
ll c[N];ll ans[N]; //c是树状数组,ans是第i次访问的答案
ll n,m,p,tot;
ll lowbit(ll i){ return i & -i; }
void update(ll i,ll v)
{
while(i<=n){
c[i] += v;
i += lowbit(i);
}
}
ll sum(ll i)
{
ll ret = 0;
while(i>0){
ret += c[i];
i -= lowbit(i);
}
return ret;
}
struct node1 // 存有值的点
{
ll x,y,w;
}point[N];
bool cmp1(node1 a,node1 b){return a.y<b.y;}
struct node2 //存访问的前缀和
{
ll x,y,id,flag;
node2(){}
node2(ll _x,ll _y,ll _id,ll _flag):
x(_x),y(_y),id(_id),flag(_flag){}
}q[N*4];
bool cmp2(node2 a,node2 b){return a.y<b.y;}
void solve()
{
memset(c,0,sizeof(c));
memset(ans,0,sizeof(ans));
sort(point+1,point+1+m,cmp1);
sort(q+1,q+1+tot,cmp2);
int k = 1;
for(int i=1;i<=tot;i++){
while(k<=m && q[i].y>=point[k].y){//有值的点没有添加完 && 访问前缀和的y值大于等于添加点的y值
if(point[k].x!=0) update(point[k].x,point[k].w);//添加点,一直到树状数组c存的是小于等于y的前缀和
k++;
}
if(q[i].x!=0) {
ans[q[i].id] += sum(q[i].x)*q[i].flag;
}
}
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
point[i].x = x; point[i].y = y; point[i].w = getDigit(getNum(x,y,n));
}
tot = 0;
for(int i=1;i<=p;i++){
int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
q[++tot] = node2(x2,y2,i,1);
q[++tot] = node2(x1-1,y1-1,i,1);
q[++tot] = node2(x1-1,y2,i,-1);
q[++tot] = node2(x2,y1-1,i,-1);
}
solve();
for(int i=1;i<=p;i++){
printf("%lld\n",ans[i]);
}
}
return 0;
}