HDU - 4466 Triangle

题目:给你一根长度为n的铁丝,将铁丝分成几部分,再把每部分都折成三角形,并且每个三角形都相似,而且三角形的边长是整数。问有多少种分法。

三角形三遍相等视为相等,三角形顺序不同视为不同

思路:设三角形3边为a,b,c,并且a<=b<=c,

令dp[i]表示周长为i的三角形有多少个

考虑周长i:

如果b=c,那么c的最长是(i-1)/2,最短是(i+2)/3,那么新增加了(i-1)/2-(i+2)/3+1个三角形;


如果b<c,那么我们可以让c在周长为i-1的情况下增加1,那么就可以增加dp[i-1]个三角形,

注意,考虑现在的c,存在a+b=c的情况,这种情况要删去,i-1=a+b+c-1==>i=2*c,因此在i为偶数时要减去i/2/2


然后我们用筛法保证gcd(a,b,c)=1,然后就可以用插板的方法算分法数了,具体看代码

代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f

const LL MOD=1e9+7;
const int maxn=5e6+50;
int dp[maxn],num[maxn];
void init(int n){
    dp[3]=1;
    for(int i=4;i<=n;i++){
        dp[i]=dp[i-1]+(i-1)/2-(i+2)/3+1;
        if(i%2==0) dp[i]-=i/2/2;
        if(dp[i]>=MOD) dp[i]-=MOD;
        if(dp[i]<0) dp[i]+=MOD;
    }
    num[0]=1;
    num[1]=2;
    num[2]=4;
    for(int i=3;i<=n;i++){//使得gcd(a,b,c)=1,为插板做准备
        num[i]=num[i-1]*2;
        if(num[i]>=MOD) num[i]-=MOD;
        for(int j=2;j*i<=n;j++){
            dp[i*j]-=dp[i];
            if(dp[i*j]<0)
                dp[i*j]+=MOD;
        }
    }
}
int main(){

    int cas=0,n;
    init(maxn-50);
    while(~scanf("%d",&n)){
        LL ans=0;
        for(int i=1;i*i<=n;i++){
            if(n%i!=0) continue;
            ans=(ans+(LL)dp[i]*num[n/i-1]%MOD)%MOD;
            if(i*i!=n)
                ans=(ans+(LL)dp[n/i]*num[i-1]%MOD)%MOD;
        }
        printf("Case %d: %lld\n",++cas,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值