題意
一個螺旋的矩陣,大小爲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;
}