[數學/多項式/推導] 200606Practice T3 square

考場上直接拼錯成sqare,英語不好沒辦法 😭

Algorithm 1

20pts,考場做法

fif_ii×ii\times i的方案數
ans=i=1min{n,m}(ni+1)(mi+1)fi ans=\sum_{i=1}^{\min\{n,m\}}(n-i+1)(m-i+1)f_i
O(NT)O(NT)
嘗試莫隊優化(然鵝並不可能)

研究fif_i,觀察5×55\times 5的正方形:
在這裏插入圖片描述

考慮對稱性,省掉一半的正方形:
在這裏插入圖片描述

發現每個正方形裏面的好感度應爲i2()i^2-(邊切掉角落上的正方形數)

邊切掉角落上的正方形數:
在這裏插入圖片描述
在這裏插入圖片描述
容易發現:
在這裏插入圖片描述
對於i×ii\times i中一條正方形的線,橫縱座標a,ba,b(且滿足a+b=ia+b=i
設與垂直於x軸直線x=j,jNx=j,j\in N^*的交點縱座標爲yjy_j,那麼(j,j+1)(j,j+1)上面對的正方形個數爲
byj \lfloor b-y_j\rfloor

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

注意到上面的方法中運用幾何精度不對,幾何運算耗時大
考慮數學推導:

對於“正”的正方形的貢獻:
i=1min(n,m)i2(ni+1)(mi+1) \sum_{i=1}^{\min(n,m)}i^2(n-i+1)(m-i+1)
對於“斜”的正方形的貢獻:
考慮用它所在的“正”的正方形減去四條邊切下來的小正方形的個數
對於一條邊,可能會經過節點,考慮gcd之後便得到gcd節不會經過節點的
發現與豎網格線相交的每個交點,向右邊有一個小正方形被切掉(不合法),agcd(a,b)1\frac{a}{\gcd(a,b)}-1
與橫網格線相交的每個交點,向上面有一個小正方形被切掉(不合法),bgcd(a,b)1\frac{b}{\gcd(a,b)}-1
再加上角落中線開始的那個放個,一共gcd(a,b)×((agcd(a,b)1)+(bgcd(a,b)1)+1)\gcd(a,b)\times((\frac{a}{\gcd(a,b)}-1)+(\frac{b}{\gcd(a,b)}-1)+1)
i=1min(n,m)(a=1b=iai1i24×abgcd(a,b)×((agcd(a,b)1)+(bgcd(a,b)1)+1)2)(ni+1)(mi+1) \sum_{i=1}^{\min(n,m)}(\sum_{a=1\\b=i-a}^{i-1}i^2-4\times\frac{ab-\gcd(a,b)\times((\frac{a}{\gcd(a,b)}-1)+(\frac{b}{\gcd(a,b)}-1)+1)}{2})(n-i+1)(m-i+1)
加起來
KaTeX parse error: \cr valid only within a tabular/array environment

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章