题目链接:https://vjudge.net/problem/%E8%AE%A1%E8%92%9C%E5%AE%A2-A2144
题目大意:
求n行三角形中等边三角形的个数,图二的三角形也算,n<=1e9
解题思路:
n范围这么大,一看就是个找规律题,那么先来打个表看看。
打表(暴力)代码:
#include<bits/stdc++.h> using namespace std; struct node{ double x, y; }a[6000]; int n, cnt; double dis(int i, int j) { return sqrt( (a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y) ); } bool check(int i, int j, int k) { double a = dis(i, j); double b = dis(i, k); double c = dis(j, k); if(fabs(a-b)<=0.0000001&&fabs(a-c)<=0.0000001&&fabs(b-c)<=0.0000001) return true; return false; } int main() { while(cin >> n) { cnt = 0; double posx = 0; double posy = 0; //从(0,0)开始 for(int i = 0; i <= n; i++) { posx = 0.5*i; for(int j = 0; j <= n-i; j++) { // cout << posx <<" "<<posy<<endl; a[cnt].x = posx; a[cnt++].y = posy; posx += 1.0; } posy += sqrt(3) / 2.0; } int ans = 0; for(int i = 0; i < cnt ; i++) { for(int j = i+1; j < cnt; j++) { for(int k = j+1; k < cnt; k++) { if(check(i, j, k)) ans++; } } } printf("%d: %d\n",n,ans); } }
打表后的结果:
1,5,15,35,70,126,210...
没感觉?
做差
4,10,20,35,56,84...
继续做差
6,10,15,21,28...
再做差
4,5,6,7... 这是一个等差数列了,那么再做一次差就是常数列了。
每一次做差都是在对离散函数的相邻两点求斜率,这很像是在求导!
我们不妨把这种过程看作是在求导,那么做差4次后(4阶导数)是常数。
因此我们设函数
代入打表的结果,解得a=1/24 , b=6/24, c=11/24, d=6/24, e=0。
提出1/24出来,
逆元+O(1)搞定。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 1e9+7; ll q_pow(ll a, ll b, ll m){ ll ans = 1; while(b > 0){ if(b & 1){ ans = ans * a % m; } a = a * a % m; b >>= 1; } return ans; } ll inv(ll a, ll p) { return q_pow(a, p-2, p); } int main() { int t; scanf("%d",&t); while(t--) { ll n; scanf("%lld",&n); ll ans = (((((n*n)%mod)*n)%mod)*n)%mod + 6*(((n*n)%mod)*n)%mod + (11*(n*n%mod))%mod + 6*n%mod; ans %= mod; ans = (ans* inv(24, mod)) % mod; printf("%lld\n",ans); } }