题意:
给出一N*N的蛇形矩阵,具体位置元素值不给你,自己找规律,然后给你M个
有效位置,P次查询,每次查询一个子矩阵中有效元素的权值和,该权值和等于对于
每个有效元素,模10拆分后相加得到的和。(注意有效点以及询问x、y所代表的含义,
是x轴,y轴)。
input 1 3 4 4 1 1 2 2 3 3 2 3 1 1 1 1 2 2 3 3 1 1 3 3 1 2 2 3output
5 18 23 17
思路:
第一个难点在于回旋矩阵给出x,y坐标让我们求对应值,找规律写通项公式。
其次查询二维区间和的难点在于10^6*10^6的矩阵无法表示,用map空间上可以,
但是map修改操作带个log,所以会超时,即使是用二维树状数组。不信看下面的超时代码。
比赛时自己的想法很接近正确思路,离线然后树状数组维护,但是因为这种题目做的
比较少,知识点不够熟练,所以没能写出来。
比赛思考如下,因为有过见过一些类似题目(见过,代码实现的基本没有),所以
很容易往离线查询上想,然后树状数组维护。因为考虑到1e6*1e6的矩阵大小,但有效点最多
只有1e6个,所以想到降维处理,二维变为一维,然后对于每一次查询,有两个限制因素,一个
在x轴上,一个在y轴上,不难想到如果我们将所有有效点按x从小到大排序,那么现在对于
每次询问来说,只需要考虑在L,R(L,R很容易确定)的连续区间内,y值位于限定范围内
的那些离散的元素即可(事实证明这里的L,R没啥实质作用,只是思考的过程产生的想法)。
显然我们的BIT要建立在y的值域上,并按照x轴顺序不断更新BIT,在当前x位置,前x列的元素
都已经被添加,所以我们可以查询任意y区间的区间和,但注意,这里求出的y的区间和是对于
1~x所有列来说的,而并不是我们所需要的 x 范围。(比赛中bug就在这里,没有考虑到去重,
通过样例发现问题后也没有想到合适的解决办法)。
一个很巧妙的解决方法是每个询问拆成两部分,两者均为1~对应x的前缀统计(差分公式拆分)。
只不过一个为正数,一个为负数,这样每次询问的答案就由两个询问共同组成。
再仔细考虑离线查询的过程,我们是先枚举有效点,然后移动询问,还是先枚举询问再移动有效点。
自己当时写的代码采取了第一种方案,但自己写的有很大bug,当x1位置被更新后,我们要计算所有
位于x1和x2之间的询问,(而不仅仅是x1相关的询问,比赛时的bug)。*****(所以说理论上两种枚举方
案都可以,不过本题第一种方案麻烦一点,要对最后一个有效位置单独处理)。看了题解大都第二种方案,
直接枚举询问,对于当前询问,把它前面所有的有效位置全部更新,这样就不用特殊处理了,较方便。
引申......上面对于询问的拆分,难道不就是线段树优化扫描线时的方法么,拆分后的项由1个x,2个y
(或1个y,2个x)组成,权值分别为1和-1 ... ...
有大佬 直接利用二维差分前缀和的公式,将一次查询拆分为四次查询:
。
然后直接按照x的顺序依次统计相应y的数量即可。虽然这样的拆分是多余的,因为只要x或者y一
者被拆分,另一者完全可以通过前缀和求解给定L,R的权值和,且无遗漏无重复。(或者说造成:
如按x排序,bit针对y轴,造成y冗余的原因是因为x的冗余,所以对x拆分即可)。但是4项拆分思
维量对目前水平的自己来说看起来好像思维量小好多,关键在于理解好4项拆分为什么是正确的?
代码实现(二维树状数组TLE):
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <map>
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;
const int N = 1e6 + 100;
map<int,long long>bit[N];
int xx[N],yy[N],tot;
void add(int x,int y,long long val,int n) {
for(int i=x; i<=n; i+=i&-i) {
for(int j=y; j<=n; j+=j&-j) {
bit[i][j]+=val;
xx[++tot]=i;
yy[tot]=j;
}
}
}
long long qu(int x,int y) {
long long res = 0;
for(int i=x; i; i-=i&-i)
for(int j=y; j; j-=j&-j) {
res+=bit[i][j];
}
return res;
}
LL sol(LL n, LL x, LL y) {
LL qs = n / 2, q = min(n - y + 1, min(n - x + 1, min(x, y))) - 1;
if (x == qs + 1 && y == qs + 1) return n * n;
LL ans = 1ll * q * (8 * qs + 8 * (qs - q + 1)) / 2;
if (n - x == q) ans += n - q - y + 1;
else if (y - 1 == q)ans += n - 2 * q + 1 + n - q - 1 - x;
else if (x - 1 == q)ans += n - 2 * q + 1 + n - 2 * q - 2 + y - q - 1;
else ans += n - 2 * q + 1 + n - 2 * q - 2 + n - 2 * q - 1 + x - q - 1;
return ans;
}
int main() {
#ifdef MYHOME_Wjvje
freopen("input.txt","r",stdin);
#endif
int t;
//cout<<(sizeof(bit))/1024<<endl;
scanf("%d",&t);
while(t--) {
while(tot)bit[xx[tot]][yy[tot]]=0,tot--;
int n,m,p;
scanf("%d%d%d",&n,&m,&p);
int x,y;
//TLE
for(int i=1; i<=m; i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y,sol(n,x,y),n);
}
//TLE too...
for(int i=1; i<=p; i++) {
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%lld\n",qu(x2,y2)-qu(x1,y1-1)-qu(x1-1,y1)+qu(x1-1,y1-1));
}
}
return 0;
}
两项拆分离线AC代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 2e5+100;
const int M = 4e5+100;
struct Node {
int r,c;
long long val;
} arr[N];
bool cmp1(Node aa,Node bb) {
return aa.r<bb.r;
}
bool cmp2(Node aa,Node bb) {
return aa.c<bb.c;
}
//回旋矩阵O(1)求值
LL sol1(LL n, LL x, LL y) {
x=x-n/2-1;
y=y-n/2-1;
long long tmp=max(abs(x),abs(y)),ans;
if(x>=y)ans=n*n-4*tmp*tmp-2*tmp-x-y;
else ans=n*n-4*tmp*tmp+2*tmp+x+y;
return ans;
}
long long sol2(long long x) {
long long res=0;
while(x) {
res+=x%10;
x/=10;
}
return res;
}
struct query {
int x1,y1,x2,y2,id;
int val;
} qrr[N];
bool cmp3(query aa,query bb) {
return aa.x2<bb.x2;
}
int bst[N],ans[N];
void update(int x,int v,int n) {
for(; x<=n; x+=x&-x)bst[x]+=v;
}
int qu(int x) {
int res=0;
for(; x; x-=x&-x)res+=bst[x];
return res;
}
int main() {
#ifdef MYHOME_Wjvje
freopen("input.txt","r",stdin);
#endif
int t,n,m,p,tot;
int x,y;
scanf("%d",&t);
while(t--) {
tot=0;
memset(bst,0,sizeof(bst));
memset(ans,0,sizeof(ans));
scanf("%d%d%d",&n,&m,&p);
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
arr[i].r=x;
arr[i].c=y;
arr[i].val=sol2(sol1(n,x,y));
}
sort(arr+1,arr+1+m,cmp1);
for(int i=1; i<=p; i++) {
scanf("%d%d",&qrr[i].x1,&qrr[i].y1);
scanf("%d%d",&qrr[i].x2,&qrr[i].y2);
qrr[i].id=i;
qrr[i].val=1;
qrr[i+p].id=i;
qrr[i+p].val=-1;
qrr[i+p].y1=qrr[i].y1;//1个 x对 2个 y.
qrr[i+p].y2=qrr[i].y2;
qrr[i+p].x2=qrr[i].x1-1;
}
sort(qrr+1,qrr+1+p*2,cmp3);
int pw=1;
//先枚举查询,另一种枚举理论上可行
for(int i=1; i<=p*2; i++) {
while(pw<=m&&arr[pw].r<=qrr[i].x2)
update(arr[pw].c,arr[pw].val,n),pw++;
ans[qrr[i].id]+=qrr[i].val*(qu(qrr[i].y2)-qu(qrr[i].y1-1));
}
for(int i=1; i<=p; i++)printf("%d\n",ans[i]);
}
return 0;
}

四项拆分离线AC代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+100;
struct Node {
int x,y;
long long val;
bool operator <(const Node & obj)const {
return x<obj.x;
}
} arr[N];
//回旋矩阵求值
long long sol1(long long n,long long x,long long y) {
x=x-n/2-1;
y=y-n/2-1;
long long tmp=max(abs(x),abs(y)),ans;
if(x>=y)ans=n*n-4*tmp*tmp-2*tmp-x-y;
else ans=n*n-4*tmp*tmp+2*tmp+x+y;
return ans;
}
long long sol2(long long x) {
long long res=0;
while(x) {
res+=x%10;
x/=10;
}
return res;
}
struct query {
int x,y,id,val;
bool operator <(const query & obj)const {
return x<obj.x;
}
} qrr[4*N];
long long bst[N],ans[N];
void update(int x,int v,int n) {
for(; x<=n; x+=x&-x)bst[x]+=v;
}
long long qu(int x) {
long long res=0;
for(; x; x-=x&-x)res+=bst[x];
return res;
}
int main() {
#ifdef MYHOME_Wjvje
freopen("input.txt","r",stdin);
#endif
int t,n,m,p;
int x,y;
scanf("%d",&t);
while(t--) {
memset(bst,0,sizeof(bst));
memset(ans,0,sizeof(ans));
scanf("%d%d%d",&n,&m,&p);
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
arr[i].x=x;
arr[i].y=y;
arr[i].val=sol2(sol1(n,x,y));
}
sort(arr+1,arr+1+m);
for(int i=1; i<=p; i++) {
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
//sum=map[x2][y2]−map[x2][y1−1]−map[x1−1][y2]+map[x1−1][y1−1]
qrr[i]=query {x2,y2,i,1};
qrr[i+p]=query {x1-1,y2,i,-1};
qrr[i+2*p]=query {x2,y1-1,i,-1};
qrr[i+3*p]=query {x1-1,y1-1,i,1};
}
sort(qrr+1,qrr+1+p*4);
int pw=1;//离线
for(int i=1; i<=p*4; i++) {
while(pw<=m&&arr[pw].x<=qrr[i].x)
update(arr[pw].y,arr[pw].val,n),pw++;
ans[qrr[i].id]+=qrr[i].val*qu(qrr[i].y);
}
for(int i=1; i<=p; i++)printf("%lld\n",ans[i]);
}
return 0;
}
THE END;