清北学堂模拟赛d3t2 b
分析:一道比较让人头疼的数学题.
先考虑怎么让分出来的三角形相似,先不考虑每个三角形的具体边长,设每个三角形的周长为li,则可知必然有一个数g = gcd{li},每一个三角形的周长都是g的倍数,这样就会有n/g个单位三角形,我们只需要把n/g分配给若干个三角形就可以了,利用隔板法,可以算出方案数为2^(n/g - 1).
再来考虑知道了周长怎么求这个周长的三角形有多少个.为了方便起见,设a ≤ b ≤ c,s = a + b + c,如果b = c,那么s = a + 2b,b的取值范围就是[g/3上取整,(g-1)/2下取整],看看取值范围内有多少个整数就有多少种方案.如果b < c,那么可以把c--,直到变成b=c,那么就是f[i] = f[i - 1],但是这样有一种特殊情况:a + b = c,这在f[i - 1]中是合法的,但是我们在处理的时候要减掉这种方案.s = 2c,c = g/2,显然只有g是偶数的时候才会出现这种情况,这时a和b只能取g/4个数,方案数减去g/4就可以了.
但是这样还是不行,如果一个三角形边长是2,2,3,另外一个是4,4,6,那么可以将前面一个三角形作为单位三角形分配给后面的三角形,直接计算会将一个方案算多次,所以我们要求的f[s],s = a + b + c中的a,b,c必须是互质的,为了去重,每一个f[s] -= f[k],k | s.就可以了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int mod = 1e9 + 7; typedef long long ll; ll n, f[1000010], p[1000010], cnt, mi[1000010], ans; void add(ll &a, ll b) { a += b; if (a >= mod) a -= mod; } int main() { scanf("%lld", &n); for (ll i = 3; i <= n; i++) { f[i] = f[i - 1]; add(f[i], (i - 1) / 2 - ceil((double)i * 1.0 / 3) + 1); if (!(i & 1)) f[i] -= i / 4; } for (ll i = 1; i * i <= n; i++) if (n % i == 0) { if (i * i != n) { p[++cnt] = i; p[++cnt] = n / i; } else p[++cnt] = i; } sort(p + 1, p + 1 + cnt); for (ll i = 1; i <= cnt; i++) for (ll j = 1; j < i; j++) if (p[i] % p[j] == 0) add(f[p[i]], mod - f[p[j]]); mi[0] = 1; for (ll i = 1; i <= n; i++) { mi[i] = mi[i - 1]; add(mi[i], mi[i - 1]); } for (ll i = 1; i <= cnt; i++) add(ans, mi[n / p[i] - 1] * f[p[i]] % mod); printf("%lld\n", ans); return 0; }