清北学堂模拟赛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
", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7650595.html