題目大意
給定一個面積 的區域,在其中放若干個邊長爲 的正方形矩陣,使得每個正方形矩陣的邊緣與和其相鄰的正方形矩陣的邊緣(或相鄰的整個大的區域的邊緣)的距離都相等,求合法的方案數。
數據範圍 。
題解
你拿到這道題,想了很久,發現只會考慮樸素算法,你嘗試着枚舉一行放 個正方形矩陣,一列放 個矩陣,其中 且 都是正整數,你發現,這對 合法的充要條件是
你考慮到除法可能會丟失精度,你化簡了一下,得到了一個優秀的式子和不優秀的暴力。
for(long long x=1;x*l<=n;x++)
for(long long y=1;y*l<=m;y++)
if((n-x*l)*(y+1)==(m-y*l)*(x+1))
ans++;
你看着那行判斷,沉思許久,你發現對於任意滿足 式的 ,都有唯一成立的 。你嘗試把 式拆開
化簡,整理,嘗試讓左邊只剩下 。
於是,你得到了一個優秀少許的做法,枚舉 ,判斷 是不是正整數。
for(long long x=1;x*l<=n;x++)
if(((m+l)*x+m-n)%(n+l)==0) ++ans;
你繼續化簡,發現沒有任何餘地,或者說,你的腦子根本不給你機會想到比這更優秀的做法,你開始放棄這種想法,並在爲幾秒鐘之前的自己白忙活了而感到幸災樂禍。你繼續考慮 式,你嘗試將它變得更加優美一些。
你定眼一看,發現 都是已知的,你令 問題變成了關於 的一元二次方程 的正整數解的個數,再 浪費時間 想想,你想到了擴展歐幾里得算法,並用它求出了不定方程的特解 ,還導出了所有解的通式
你發現你要求的,便是使得 均爲正整數的整數 的個數。將這個限制代入,得
你分別討論了兩個式子化簡時乘除的東西的正負號,寫出了這樣的代碼。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int exgcd(int a,int b,long long &x,long long &y) {
if(!b) return x=1,y=0,a;
int d=exgcd(b,a%b,y,x);
y-=a/b*x; return d;
}
int Dioequ(int a,int b,int c,long long &x,long long &y){
int d=exgcd(a,b,x,y);
int k=c/d; x*=k;y*=k;
return d;
}
int n,m,l;
signed main(){
scanf("%lld%lld%lld",&n,&m,&l);
int a=m+l,b=-n-l,c=n-m,x1,y1;
int gcd=Dioequ(a,b,c,x1,y1);
double la=(double)(1-x1)/(b/gcd);//計算第一條式子的左側<=1整理後的結果
double lb=(double)(1-y1)/(-a/gcd);//計算第二條式子的左側<=1整理後的結果
double ra=(double)(n/l-x1)/(b/gcd);//計算第以條式子的左側>=n/l整理後的結果
double rb=(double)(m/l-y1)/(-a/gcd);//計算第二條式子的左側>=m/l整理後的結果
if(-a/gcd<0) swap(lb,rb);//判斷正負
if(b/gcd<0) swap(la,ra);
int up=(int)floor(min(ra,rb));
int dn=(int)ceil(max(la,lb));
if(min(ra,rb)<max(la,lb)) return printf("0\n")==1;//判斷無解
printf("%lld\n",up-dn+1);
return 0;
}
你對這份代碼信心滿滿,並決定這份代碼交上去,本以爲需要調半天,結果發現過了……