P4676 [BalticOI 2016 day1] Spiral
思路
数学题。
先定义象限包括对应的坐标轴上的格点。
对于查询的每一个矩形,分别计算此矩形在每个象限覆盖的格点的权值总和,最后减去重复计算的坐标轴格点的权值。
如图所示,将查询的矩形分为四个象限处理。
以第一象限为例。
设查询的矩形左下角为
设
当第一象限内有格点被覆盖时,存在三种情况:
容易证明,第一象限对答案的贡献均为:
因此我们需要求出任一
设
设
设
此时分三种情况讨论:
-
当
x=y 时,F(x,y)=f(x)=f(y) 。 -
当
x>y 时,观察所求区域,将其分成两部分求解:
深色部分即
即
此时
- 当
x<y 时,同理,在浅色区域中,每一行均为公差为1 的等差数列。
此时
对于第二、三、四象限,我们只需旋转坐标系,将所求象限转化至第一象限进行求解即可(在代码实现中体现为变换矩形对应顶点的坐标)。这样可以保证需要计算的横纵坐标均为自然数。
每个象限的
对于第一象限的
同理,第二象限用
接下来需要推导每个象限的
下面给出
以推导
同理,我们有:
下面给出
以第一象限为例。
如图所示,将原图分层。定义
观察到,第
同理,在第二象限的计算中:
在第三象限的计算中:
第四象限较为特殊。
如图,将每一层分成红色和蓝色两部分。
在第
将
至此完成所有象限中
最终,对每一个被覆盖的象限进行计算,并减去重复计算的部分。
设查询的矩形左下角为
-
当原点被覆盖(即查询的矩形覆盖了
4 个象限)时,坐标轴(除原点)上被覆盖的格点被统计了2 次,原点被统计了4 次。此时ans 应减去g_3(c)+g_0(d)+g_1(-a)+g_2(-b)-1 。 -
当原点未被覆盖且有坐标轴被覆盖(即查询的矩形覆盖了
2 个象限)时,坐标轴上的格点被统计了2 次。
以此情况为例。此时
其他情况同理,减去重叠部分即可。
- 当原点未被覆盖且无坐标轴被覆盖(即查询的矩形仅覆盖
1 个象限)时,无重复计算的部分。
至此完成全部推导。
代码
取模用 modint。对于除法,算出除数的逆元。
#include<bits/stdc++.h>
#define int long long
#define TT 1000000007
#define _0 (modint){0}
#define _1 (modint){1}
#define _2 (modint){2}
#define _3 (modint){3}
#define _4 (modint){4}
#define d2 (modint){500000004}
#define d3 (modint){333333336}
#define M(x) (modint){x}
using namespace std;
int n,q,vis[4];
struct node{
int x,y;
}a[4];
struct modint{
int v;
modint operator+(const modint &b)const{return (modint){(v+b.v)%TT};}
modint operator-(const modint &b)const{return (modint){((v-b.v)%TT+TT)%TT};}
modint operator*(const modint &b)const{return (modint){(v*b.v)%TT};}
}ans;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')ret=ret*10+(ch&15),ch=getchar();
return ret*f;
}
modint f(int op,modint x){
if(op==3)return _2*x*x*(x+_1)*(x+_1)+x+_1;
if(op==0)return _2*x*x*(x+_1)*(x+_1)+_2*x*(x+_1)*(_2*x+_1)*d3+x*(x+_1)+x+_1;
if(op==1)return _2*x*x*(x+_1)*(x+_1)+_4*x*(x+_1)*(_2*x+_1)*d3+_2*x*(x+_1)+x+_1;
if(op==2)return _2*x*x*(x+_1)*(x+_1)+_2*x*(x+_1)*(_2*x+_1)*d3+_3*x*(x+_1)+x+_1;
}
modint g(int op,modint x){
if(op==3)return _2*x*(x+_1)*(_2*x+_1)*d3-_3*x*(x+_1)*d2+x+_1;
if(op==0)return _2*x*(x+_1)*(_2*x+_1)*d3-x*(x+_1)*d2+x+_1;
if(op==1)return _2*x*(x+_1)*(_2*x+_1)*d3+x*(x+_1)*d2+x+_1;
if(op==2)return _2*x*(x+_1)*(_2*x+_1)*d3+_3*x*(x+_1)*d2+x+_1;
}
modint F(int op,modint x,modint y){
if(x.v<0||y.v<0)return _0;
if(x.v==y.v)return f(op,x);
if(x.v>y.v)return f(op,y)+(_2*g(op,x)-_2*g(op,y)+(x-y)*y)*(y+_1)*d2;
return f(op,x)+(_2*g((op+1)%4,y)-_2*g((op+1)%4,x)-(y-x)*x)*(x+_1)*d2;
}
void calc(int op){
int sx=0,sy=0,ex=0,ey=0;
if(op==3)sx=a[1].x,sy=a[1].y,ex=a[3].x,ey=a[3].y;
if(op==0)sx=a[2].y,sy=-a[2].x,ex=a[0].y,ey=-a[0].x;
if(op==1)sx=-a[3].x,sy=-a[3].y,ex=-a[1].x,ey=-a[1].y;
if(op==2)sx=-a[0].y,sy=a[0].x,ex=-a[2].y,ey=a[2].x;
ans=ans+F(op,M(ex),M(ey))-F(op,M(ex),M(sy-1))-F(op,M(sx-1),M(ey))+F(op,M(sx-1),M(sy-1));
}
signed main(){
n=read(),q=read();
while(q--){
ans=_0;
for(int i=0;i<=3;i++)vis[i]=0;
a[1].x=read(),a[1].y=read(),a[3].x=read(),a[3].y=read();
a[0].x=a[1].x,a[0].y=a[3].y,a[2].x=a[3].x,a[2].y=a[1].y;
if(a[0].x<=0&&a[0].y>=0)calc(0),vis[0]=1;
if(a[1].x<=0&&a[1].y<=0)calc(1),vis[1]=1;
if(a[2].x>=0&&a[2].y<=0)calc(2),vis[2]=1;
if(a[3].x>=0&&a[3].y>=0)calc(3),vis[3]=1;
if(vis[0]&&vis[1]&&vis[2]&&vis[3])ans=ans-(g(3,M(a[3].x))+g(0,M(a[3].y))+g(1,M(-a[1].x))+g(2,M(-a[1].y))-_1);
else{
if(vis[3]&&vis[0])ans=ans-(g(0,M(a[3].y))-g(0,M(a[1].y-1)));
if(vis[0]&&vis[1])ans=ans-(g(1,M(-a[1].x))-g(1,M(-a[3].x-1)));
if(vis[1]&&vis[2])ans=ans-(g(2,M(-a[1].y))-g(2,M(-a[3].y-1)));
if(vis[2]&&vis[3])ans=ans-(g(3,M(a[3].x))-g(3,M(a[1].x-1)));
}
printf("%lld\n",ans.v);
}
return 0;
}
单次询问为