洛谷3166 數三角形
傳送門
Description:
給定一個nxm的網格,請計算三點都在格點上的三角形共有多少個。下圖為4x4的網格上的一個三角形。註意三角形的三點不能共線。
n,m<=1000
這裏是超級優秀的題解啊
再整理一下題解的思路:
首先 把我們要找的三角形剛好框到一個矩形裏
也就是說在這個矩形的邊上任意找兩點連線一定能把三角形分成兩部分
可以發現 對於各種不同的子矩形 找框在其中的三角形數量的方法是一樣的(後面再講)
所以我們現在的任務就是求子矩形的數量
題解裏是這樣做的:可以發現我們並不關心矩形在哪裏 所以不必要枚舉起點終點
我們只關心矩形的長x寬y以及這樣的矩形有多少個
所以枚舉x,y 矩形數為(n-x+1)*(m-y+1)
(n-x+1)*(m-y+1)這個式子是如何得到的呢 畫圖就知道啦(下圖)
把黃色矩形平移得到藍色的 紅色的 etc.很好理解
然後現在我們就把問題轉化為了給定知道長寬的矩形 求框在裏面的三角形數
現在就要分類討論啦用ABC來表示三角形,x為AE的長,y為AG的長
首先很容易知道的是 三角形的一頂點一定在矩形的頂點上 令這點為A
1. B,C 兩點都不在頂點上,則B一定在EF上 C一定在FG上,且A可以取在四個頂點
所以這種情況的方案數為 4*(x-1)*(y-1)
2.B與E重合 方案數為x-1
3.C與G重合 方案數為y-1
2,3兩種情況其實就是三角形有兩個頂點在矩形頂點上且相連不是對角線
所以2,3的方案數之和為2*(x-1+y-1)
4.B或C與F重合 不妨取B與F重合
這時C可以取矩形裏的任何格點(先不考慮三點共線 )
則方案數為(x+1)*(y+1)
然後我們要減掉三點共線的情況 也就是求AB經過的格點數(包括本身)
AB經過的格點數為 gcd(x,y)+1
原因:(不嚴謹請指出,因為題解沒證 我自己想的)
如圖,矩形對角線DF經過四個格點
除本身以外,取經過的點中最上方的點H向DG作垂線垂足為I
則三角形DHI一定相似於三角形DGF
然後我們發現 y/DI-1即為經過的格點數(除A,B) 我們只要求出DI即可
設DI為z 則x/y*z(即為HI)必定為整數
又要z最小 所以z=y/gcd(x,y) 則y/z=gcd(x,y)
綜上 長為x寬為y的矩形的對角線經過的格點個數為 gcd(x,y)+1
又因為矩形的對角線有兩條
所以最後這種情況的方案數為2*[(x+1)*(y+1)-gcd(x,y)-1]
把幾種情況的方案數加起來 為 4*(x-1)*(y-1)+2(x-1+y-1)+2*[(x+1)*(y+1)-gcd(x,y)-1]
化簡之後為6*x*y-2*gcd(x,y)
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #define R register 4 #define go(i,a,b) for(R int i=a;i<=b;i++) 5 #define ll long long 6 using namespace std; 7 int read() 8 { 9 int x=0,y=1;char c=getchar(); 10 while(c<‘0‘||c>‘9‘) {if(c==‘-‘) y=-1;c=getchar();} 11 while(c>=‘0‘&&c<=‘9‘) {x=(x<<1)+(x<<3)+c-‘0‘;c=getchar();} 12 return x*y; 13 } 14 ll ans,n,m; 15 int gcd(int x,int y) 16 { 17 return x%y==0?y:gcd(y,x%y); 18 } 19 int main() 20 { 21 n=read();m=read(); 22 go(i,1,n) go(j,1,m) ans+=(n-i+1)*(m-j+1)*(6*i*j-2*gcd(i,j)); 23 printf("%lld",ans); 24 return 0; 25 }View Code
洛谷3166 數三角形