HDU 5136 DP

HDU 5136
题目链接:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=93697#problem/J
题意:
问满足 一个连通图中两点之间最大距离是n的图有多少种。限制条件是一个点最多连三个点。
思路:
比赛的时候想到了以最长链为基础,在结点上加边。然而由于树同构的模型不是很清晰自己推的样例都退错,更不用说公式了。
主要是推公式,中间各种漏分母、漏项、爆long long.
正题:
假设一个长度为i的分支有dp[i]种,长度小于等于i的分支总数有num[i]种。
则对于一个询问ans,分奇数偶数两种。
偶数:保证两边取得分支不会重复就可,为dp[i/2]*(dp[i/2]-1)+dp[i](选不同和相同两种)
奇数:比较复杂,因为不能均匀切分所以把分成中点和两个长度确定为(i-1)/2的分支。分为三种
1)中间节点接的分支,分支长度小于(i-1)/2,则ans+=C(dp[(i-1)/2,2]*num[(i-1)/2-1]
2)中间结点接的分支,长度等于(i-1)/2,设M=dp[(i-1)/2],则ans+=C(M,3)+C(M,2)*2+C(M,1),原理和偶数的时候相同。
那么怎么推出dp和num呢?
num可以由dp和求出,根据前面的容斥的思想,dp[i] = dp[i-1]*num[i-2]+C(dp[i-1],2),自己写的时候总是把分支为dp值和ans值搞混……
源码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
#define LL long long
#define MOD (1000000007)
#define gmax(a,b) ((a) > (b) ? (a) : (b))
const int MAXN = 100000 + 4;
LL dp[MAXN];
LL sum[MAXN];
LL ppow(int a, int b)
{
    LL ans = 1;
    LL u = a, x = b;
    while(x){
//        printf("a = %d, b = %d, ans = %lld\n", a, b, ans);
        if(x & 1)   ans = (ans * u) % MOD;
        u = (u * u) % MOD;
        x >>= 1;
    }
    return ans;
}
void init()
{
    dp[1] = 1;
    sum[1] = 2;
    sum[0] = 1;
    dp[0] = 1;
    for(int i = 2 ; i < MAXN ; i++){
        dp[i] = dp[i - 1] * sum[i - 2] % MOD + (dp[i - 1]) * (dp[i - 1] + 1) % MOD * ppow(2, MOD - 2) % MOD;
        dp[i] %= MOD;
        sum[i] = (sum[i - 1] + dp[i]) % MOD;
    }
//    printf("dp[2] = %lld\n", dp[2]);
//    printf("dp[3] = %lld, dp[4] = %lld\n", dp[3], dp[4]);
}
LL cal(int n)
{
    LL ans;
    if(n % 2 == 0){
        ans = dp[n / 2] * (dp[n / 2] - 1) % MOD * ppow(2, MOD - 2) % MOD + dp[n / 2];
    }
    else{
        int mark = (n - 1) / 2;
        LL t1 = (sum[mark - 1] * ((dp[mark]) * (dp[mark] - 1) % MOD * ppow(2, MOD - 2) % MOD + dp[mark]) % MOD) % MOD;
        LL t2 = dp[mark] * (dp[mark] - 1) % MOD * (dp[mark] - 2) % MOD * ppow(6, MOD - 2) % MOD;
        LL t3 = dp[mark] * (dp[mark] - 1) % MOD;
//        printf("t1 = %lld, t2 = %lld, t3 = %lld\n", t1, t2, t3);
//        printf("mark = %d, dp[mark] = %lld\n", mark, dp[mark]);
//        printf("sum[mark - 1] = %lld\n", sum[mark - 1]);
        ans = (t1 + t2 + t3 + dp[mark]) % MOD;
    }
    ans %= MOD;
    return ans;
}
int main()
{
    init();
    int n;
    while(scanf("%d", &n) != EOF && n){
        if(n == 1){
            printf("1\n");
            continue;
        }
        cout << cal(n) << endl;
    }
    return 0;
}

你可能感兴趣的:(HDU 5136 DP)