HDU 4466 Triangle 第二次积分赛C题(思维+简单dp+细心)

         题目大意:

         给一根长度为n的铁丝,将它分为若干份,不过需要每一份都相似。

     解题思路:

         先求出周长为n的三角形可以分成多少互质的三角形,然后再用对小三角形插隔板法。

     题目地址: Triangle

/*
      长度为n的铁丝折断成若干相似的三角形。
      先计算长度为x的铁丝能组成的相似三角形,再dp。
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MO = (int)1e9+7;
const int N=5000005;

int f[N],fac[N];
//f(x)表示周长为x的不同三角形(a,b,c)的数量. 令a <= b <= c 

void cal()
{
    f[3]=1;
    for(int i=4;i<N;i++)
    {
        f[i]=f[i-1] + (i-1)/2 - i/3 + (i%3?0:1);
        /*b<c时,由于a,b,c为三角形,则a,b,c-1也为三角形,故用dp递推,
         但是下面要减一个特殊的
         b=c时 c(max)=fool((x-1)/2), c(min)=ceil(x/3) 所有情况为c(max)-c(min)+1;
         */
        if(!(i&1)) f[i]-=i/4; //去掉(i-1+1)/4;
        /*a+b==c的时候有a+b+c=x,则x=2*c。只有x为偶数时,这个条件才会出现
          a的取值为为(1~x/4)*/
        if(f[i]>=MO) f[i]-=MO;
        if(f[i]<0) f[i]+=MO;
    }
    fac[1]=1;fac[2]=2;
    for(int i=3;i<N;i++)
    {
        fac[i]=fac[i-1]<<1;       //fac[i]表示将i个小三角形用插隔板法的数目为fac[i]=2^(i-1)
        if(fac[i]>=MO) fac[i]-=MO;
        for(int j=2;i*j<N;j++)     //线性筛选本质三角形gcd(a,b,c)=1;
        {
            int t=i*j;
            f[t]-=f[i];
            if(f[t]<0) f[t]+=MO;
            //f[6]=f[6]-f[3]; f[9]=f[9]-f[3];
            //f[8]=f[8]-f[4]; f[12]=f[12]-f[4];
        }
    }
}

int main()
{
    int cas=0,n;
    cal();
    while(~scanf("%d",&n))
    {
        __int64 res=0;
        for(int i=1;i*i<=n;i++)   //代码优化,由于n到了10^6,n/i=j,则对称的可以得到n/j=i;
        {
            if(n%i==0)
            {
               res=(res+(__int64)f[i]*fac[n/i])%MO;
               if(i*i!=n)
                  res=(res+(__int64)f[n/i]*fac[i])%MO;
            }
        }
        /*for(int i=1;i<=n;i++)
            if(n%i==0)
               res=(res+(__int64)f[i]*fac[n/i])%MO;
          //其实可以写成n/2,不过还是没有sqrt(n)优化的好。
        }*/
        printf("Case %d: %I64d\n",++cas,(res+MO)%MO);
    }
    return 0;
}


你可能感兴趣的:(dp,思维,细心,积分赛)