考场上直接拼错成sqare,英语不好没办法 😭
Algorithm 1
20pts,考场做法
:的方案数
尝试莫队优化(然鹅并不可能)
研究,观察的正方形:
考虑对称性,省掉一半的正方形:
发现每个正方形里面的好感度应为
边切掉角落上的正方形数:
容易发现:
对于中一条正方形的线,横纵座标(且满足)
设与垂直于x轴直线的交点纵座标为,那么上面对的正方形个数为
20pts code_slow
#include<bits/stdc++.h>
#define re register
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e6+10;
const int MOD=100000007;
const int eps=1e-6;
int T;
struct Query{
int n,m;
int id;
int ans;
}q[NNN];
struct Point{
int x,y;
Point(){}
Point(int _x,int _y){x=_x,y=_y;}
};
struct Line{
double k,b;
Line(){}
Line(Point u,Point v){
k=1.0*(u.y-v.y)/(u.x-v.x);
b=u.y-k*u.x;
}
inline int y(int x,int h){
double s=k*x+b;
if(fabs(s-int(s))<eps)return h-int(s);
else return int(h-s);
}
}l;
int up;
int f[NNN];
inline int get_f(int len){
int res=0;
// for(re int x=((len&1)?((len>>1)+1):(len>>1));x<=len;++x){//注意对称性
for(re int x=1;x<=len;++x){
int y=len-x,out/*对于(0,y),(x,0)为对角线的小矩形,被切掉的/右上角的*/=0;
l=Line(Point(0,y),Point(x,0));
// printf("y=%lfx+%lf\n",l.k,l.b);
for(re int i=0;i<x;++i){
out+=l.y(i,y);
// printf("%d ",l.y(i,y));
}
out/*对于整个正方形,被切掉的/四个角落里面的*/=4*(x*y-out);
// if((!(x^y))||(!y))res=(res+(len*len-out))%MOD;
// else
res=((res+(len*len-out))/**2*/)%MOD;
}
return res;
}
int main(){
freopen("sqare.in","r",stdin);
freopen("sqare.out","w",stdout);
T=in;
for(re int i=1;i<=T;++i)
q[i].n=in,q[i].m=in,q[i].id=i,
up=max(up,max(q[i].n,q[i].m));
for(re int i=1;i<=up;++i)
f[i]=get_f(i);
for(re int i=1;i<=T;++i){
up=min(q[i].n,q[i].m);
for(re int j=1;j<=up;++j)
q[i].ans=(q[i].ans+(q[i].n-j+1)*(q[i].m-j+1)*f[j])%MOD;
printf("%d\n",q[i].ans);
}
return 0;
}
//int main(){
// for(re int i=1;i<=5;++i)
// printf("%d\n",get_f(i));
//}
20pts code_a_little_bit_quicker
#include<bits/stdc++.h>
#define re register
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e6+10;
const int MOD=100000007;
const int eps=1e-6;
int T;
struct Query{
int n,m;
int id;
int ans;
}q[NNN];
struct Point{
int x,y;
Point(){}
Point(int _x,int _y){x=_x,y=_y;}
};
struct Line{
double k,b;
Line(){}
Line(Point u,Point v){
k=1.0*(u.y-v.y)/(u.x-v.x);
b=u.y-k*u.x;
}
inline int y(int x,int h){
double s=k*x+b;
if(fabs(s-int(s))<eps)return (h-int(s))%MOD;
else return (int(h-s))%MOD;
}
}l;
int up;
int f[NNN];
inline int get_f(int len){
int res=0;
for(re int y=(len>>1);y>=0;--y){//注意对称性
// for(re int x=1;x<=len;++x){
int x=len-y,out/*对于(0,y),(x,0)为对角线的小矩形,被切掉的/右上角的*/=0;
l=Line(Point(0,y),Point(x,0));
// printf("y=%lfx+%lf\n",l.k,l.b);
for(re int i=0;i<x;++i){
out=(out+l.y(i,y))%MOD;
// printf("%d ",l.y(i,y));
}
out/*对于整个正方形,被切掉的/四个角落里面的*/=4*(x*y-out);
if((!(x^y))||(!y))res=(res+(len*len-out))%MOD;
else
res=((res+(len*len-out)*2))%MOD;
}
return res%MOD;
}
int main(){
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
T=in;
for(re int i=1;i<=T;++i)
q[i].n=in,q[i].m=in,q[i].id=i,
up=max(up,max(q[i].n,q[i].m));
for(re int i=1;i<=up;++i)
f[i]=get_f(i);
for(re int i=1;i<=T;++i){
up=min(q[i].n,q[i].m);
for(re int j=1;j<=up;++j)
q[i].ans=(q[i].ans+(q[i].n-j+1)*(q[i].m-j+1)%MOD*f[j])%MOD;
printf("%d\n",q[i].ans);
}
return 0;
}
Algorithm 2
注意到上面的方法中运用几何精度不对,几何运算耗时大
考虑数学推导:
对于“正”的正方形的贡献:
对于“斜”的正方形的贡献:
考虑用它所在的“正”的正方形减去四条边切下来的小正方形的个数
对于一条边,可能会经过节点,考虑gcd之后便得到gcd节不会经过节点的
发现与竖网格线相交的每个交点,向右边有一个小正方形被切掉(不合法),
与横网格线相交的每个交点,向上面有一个小正方形被切掉(不合法),
再加上角落中线开始的那个放个,一共
加起来
KaTeX parse error: \cr valid only within a tabular/array environment