考場上直接拼錯成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