zoukankan      html  css  js  c++  java
  • 《hdu-6540》

    这题中间转移一直没想明白,一直卡壳。

    状态dp[i][j] - i向下最深距离为j的方案数。

    状态设置对了,但是发现,子树一直转移维护的时候,枚举i,j一直当成了i + j + 1的深度了。

    但是这个其实是距离,我们需要维护的应该是max(i,j + 1)的深度。这样才能转移。

    但是这样还是不好考虑,因为这个点u如果是关键点的话,就会非常特殊。

    但是我们可以发现,u的子树组成的方案一定会经过u,那么我们可以先不管u的方案,最后再所有方案都 * 2即可。

    这里转移有两种形式:

    1 :子树一直转移。

    2 :子树和u组成的贡献,这里也是很关键的一步。

    首先这里我们可以枚举i,j去转移,但是这样显然是n ^ 2会T,我们可以考虑维护一下每个点能往下走的最远距离,降低一下枚举的上限。

    这样就不会T了,但是观察第二维就可以发现,我们可以做一个前缀和来实现O(n)的转移。

    然后就是很关键的地方:由于这题数据太弱了,很多假代码都过了,包括我一开始写的也是。

    看下面这组数据。

    4 4 1
    1 2
    1 3
    2 4
    1 2 3 4
    输出:7
    看了网上大部分的题解代码都是5,和我一开始一样。
    假代码处理答案:
    LL ans = 0;
        for(int i = 0;i <= k;++i) ans = ADD(ans,dp[1][i]);
    这是因为没有算上子树中满足条件的答案。
    因为我们这样算,必定就要经过1.但是不经过1满足的方案数都没算入。
    考虑维护一个超过k的方案数。
    如果子树中这个距离的方案数 != 0,那么就直接转移上来。说明他是以某个子树为中间点的合法方案数。
    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair<int,int> pii;
    typedef tuple<int,int,int> tu;
    const int N = 5e3 + 5;
    const int M = 1e3 + 5;
    const double eps = 1e-10;
    const LL Mod = 1e9 + 7;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    inline int read() {
        int f = 1;int x = 0;char c = getchar();
        while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
    inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
    inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
    inline long long MUL(long long x,long long y) {return x * y % Mod;}
    
    int n,m,k;
    vector<int> G[N];
    bool vis[N];
    LL dp[N][N];//dp[i][j] - 距离i最大距离为j的方案数
    LL f[N];
    int mxd[N];
    void dfs(int u,int fa) {
        for(auto v : G[u]) {
            if(v == fa) continue;
            dfs(v,u);
            memset(f,0,sizeof(f));
            for(int i = 0;i <= min(k,mxd[u]);++i) {
                for(int j = 0;j <= min(k,mxd[v]);++j) {
                    if(i + j + 1 <= k) f[max(i,j + 1)] = ADD(f[max(i,j + 1)],MUL(dp[u][i],dp[v][j]));
                }
            }
            mxd[u] = max(mxd[u],mxd[v] + 1);
            for(int i = 1;i <= mxd[u];++i) dp[u][i] = ADD(dp[u][i],dp[v][i - 1]);
            for(int i = 0;i <= mxd[u];++i) dp[u][i] = ADD(dp[u][i],f[i]);
        }
        if(vis[u] == 1) {
            for(int i = 0;i <= k;++i) dp[u][i] = MUL(2,dp[u][i]);
            dp[u][0]++;
        }
        //for(int i = 0;i <= min(mxd[u],k);++i) printf("dp[%d][%d] is %lld
    ",u,i,dp[u][i]);
    }
    void solve() { 
        n = read(),m = read(),k = read();
        for(int i = 1;i < n;++i) {
            int u,v;u = read(),v = read();
            G[u].push_back(v);
            G[v].push_back(u);
        }    
        for(int i = 1;i <= m;++i) {
            int x = read();
            vis[x] = 1;
        }
        dfs(1,0);
        LL ans = 0;
        for(int i = 0;i <= mxd[1];++i) ans = ADD(ans,dp[1][i]);
        printf("%lld
    ",ans);
    } 
    int main() {    
        solve();
        system("pause");
        return 0;
    }
    /*
    4 4 2
    1 2
    1 3
    2 4
    1 2 3 4
    
    4 4 1
    1 2
    1 3
    2 4
    1 2 3 4
    */
    View Code
  • 相关阅读:
    LeetCode——Generate Parentheses
    LeetCode——Best Time to Buy and Sell Stock IV
    LeetCode——Best Time to Buy and Sell Stock III
    LeetCode——Best Time to Buy and Sell Stock
    LeetCode——Find Minimum in Rotated Sorted Array
    Mahout实现基于用户的协同过滤算法
    使用Java对文件进行解压缩
    LeetCode——Convert Sorted Array to Binary Search Tree
    LeetCode——Missing Number
    LeetCode——Integer to Roman
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/15260228.html
Copyright © 2011-2022 走看看