zoukankan      html  css  js  c++  java
  • 「HDU6643」Ridiculous Netizens

    题目

    点这里看题目。

    分析

    我们可以先将树变成有根树,这样我们可以计算包含某个点的连通块数量,求和自然是树上所有连通块的数量。

    那么,如果一个结点被连通块包含,则它的祖先也必须被包含。自顶向下的 DP 难以使用 DFS 解决,因此我们可以在 DFS 序上进行 DP,这样选择一个结点就是转移到 DFS 上的下一个,而跳过一个结点就相当于跳过整棵子树,快速跳转即可。

    一个容易想到的 DP 是,设 \(f_{i,j}\) 表示考虑 DFS 上前 \(i\) 个结点后,当前的积为 \(j\) 时连通块个数,但是硬转移会 T。注意到,真正影响转移的是 \(\lfloor\frac{m}{j}\rfloor\) 而不是 \(j\),而 \(\lfloor\frac m j\rfloor\) 只有 \(O(\sqrt m)\) 种取值,因此我们压缩状态,设 \(g_{i,j}\) 表示考虑 DFS 上前 \(i\) 个结点后,当前的积除 \(m\) 后取整得到 \(j\) 时的连通块个数,转移便是 \(O(n\sqrt m)\) 的。

    注意到,根的选择也会影响到转移的效率,容易想到选取重心效率更高。复杂度便是 \(O(n\sqrt m\log n)\)

    小结:

    1. 思考的思路应当自然而清晰。虽然算法最终实现是点分治的样子,但是实际上思考地时候自底向上会更加自然,也容易理解。
    2. 注意 DP 过程中在 DFS 序上 DP 和优化 DP 状态的两个部分,比较有借鉴的价值。

    代码

    #include <cstdio>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    const int mod = 1e9 + 7;
    const int MAXN = 2005, MAXM = 1e6 + 5;
    
    template<typename _T>
    void read( _T &x ) {
        x = 0; char s = getchar(); bool f = false;
        while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
        while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
        if( f ) x = -x;
    }
    
    template<typename _T>
    void write( _T x ) {
        if( x < 0 ) putchar( '-' ), x = -x;
        if( 9 < x ) write( x / 10 );
        putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b ) {
        return a > b ? a : b;
    }
    
    struct Edge {
        int to, nxt;
    }Graph[MAXN << 1];
    
    int dp[MAXN][MAXN];
    int val[MAXN], mp[MAXM];
    int tot = 0;
    
    int sub[MAXN], seq[MAXN];
    int ID = 0;
    
    int head[MAXN], wei[MAXN];
    int siz[MAXN]; bool vis[MAXN];
    
    // siz[] is for division process
    
    int ans = 0;
    int N, M, cnt = 1;
    
    inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
    inline void Upt( int &x, const int v ) { x = Add( x, v ); }
    
    void AddEdge( const int from, const int to ) {
        Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
        head[from] = cnt;
    }
    
    void Clear() {
        ans = 0, cnt = 1;
        rep( i, 1, N ) {
            head[i] = 0, vis[i] = false;
        }
    }
    
    int GetCen( const int u, const int fa, const int all ) {
        siz[u] = 1; int mx = 0, ret = 0;
        for( int i = head[u], v ; i ; i = Graph[i].nxt )
            if( ( v = Graph[i].to ) ^ fa && ! vis[v] ) {
                ret |= GetCen( v, u, all );
                siz[u] += siz[v], mx = MAX( mx, siz[v] );
            }
        if( MAX( mx, all - siz[u] ) * 2 <= all ) ret = u;
        return ret;
    }
    
    // DO NOT CONFUSE sub[] with siz[].
    
    void DFS( const int u, const int fa ) {
        sub[u] = 1, seq[++ ID] = u;
        for( int i = head[u], v ; i ; i = Graph[i].nxt )
            if( ( v = Graph[i].to ) ^ fa && ! vis[v] ) 
                DFS( v, u ), sub[u] += sub[v];
    }
    
    void Calc( const int rt ) {
        ID = 0, DFS( rt, 0 );
        rep( i, 0, ID ) 
            rep( j, 0, tot ) 
                dp[i][j] = 0;
        dp[1][mp[M / wei[rt]]] = 1;
        for( int i = 1 ; i < ID ; i ++ )
            for( int j = 1 ; j <= tot ; j ++ ) {
                if( ! dp[i][j] ) continue;
                int cur = val[j], nxt = seq[i + 1];
                Upt( dp[i + sub[nxt]][j], dp[i][j] );
                if( wei[nxt] <= cur )
                    Upt( dp[i + 1][mp[cur / wei[nxt]]], dp[i][j] );
            }
        rep( j, 1, tot ) ans = Add( ans, dp[ID][j] );
    }
    
    void Divide( int u, const int all ) {
        u = GetCen( u, 0, all );
        vis[u] = true, Calc( u );
        for( int i = head[u], v ; i ; i = Graph[i].nxt )
            if( ! vis[v = Graph[i].to] )
                Divide( v, siz[v] > siz[u] ? all - siz[u] : siz[v] );
    }
    
    int main() {
        int T; read( T );
        while( T -- ) {
            read( N ), read( M ), Clear();
            rep( i, 1, N ) read( wei[i] );
            rep( i, 1, N - 1 ) {
                int u, v; read( u ), read( v );
                AddEdge( u, v ), AddEdge( v, u );
            }
            tot = 0;
            for( int l = 1, tmp ; l <= M ; l = M / tmp + 1 )
                tmp = M / l, val[mp[tmp] = ++ tot] = tmp;
            Divide( 1, N );
            write( ans ), putchar( '\n' );
        }
        return 0;
    }
    
  • 相关阅读:
    【笔记】正则表达式
    领养信息详情
    CCF201503-3(python)
    RBZ general course notes
    接入腾讯 验证码
    数据分析工具GrowingIO的使用
    js中的位运算
    es6中类中的静态属性、实例属性、静态方法、实例方法的个人理解
    分析 腾讯的 实时音视频web端demo代码
    摸索在web端使用腾讯云的实时音视频;跑通web端demo
  • 原文地址:https://www.cnblogs.com/crashed/p/15530973.html
Copyright © 2011-2022 走看看