HDU 5513 Efficient Tree 生成树计数+状态压缩

学的算法不多,这是自己的第一份题解
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5513
题意比较复杂,给你一个网格图及各边权值, n*m,n<800, m<8, 问要生成一棵包含所有格点的权值最小的生成树,权值最小是多少。
还问了一个,就是对于这个最小生成树,每个格点有一个值,值为1+(有向上连边+1, 有向左连边+1), 要把所有格点的值全部乘起来,另外如果有多棵最小生成树,就把所有权值也都乘起来。。。听上去就比较复杂
用滚动数组,g[0/1][st]表示更新到这个点时现在的生成树的权值,st表示前m个点的联通状态
将网格线从左上往右下扫,st先用最小表示法,枚举出这个点前面m个点的联通状态,然后将这些用8进制压起来(m<8), 对于现在这个点i,实际上和他右边相连的只有i-1和i-m两个点,四种情况进行枚举更新,注意每种情况的更新有要求,比如更新完后第i-m个点一定要放入连通块中,并且连的时候不能成环
还有一个要注意的就是可以把所有的转移状态都预处理出来
原题 NOI2007 生成树计数http://www.lydsy.com/JudgeOnline/problem.php?id=1494
有很多博客有Tree定理,是关于生成树的,很值得学习
一个大神的题解
http://blog.csdn.net/whjpji/article/details/7617796


#include

#include

#include

#include

typedef __int64 ll;

using namespace std;

const int mod = 1e9+7;

int left[805][10], up[805][10], v[1005], a[8], b[8];

ll f[2][1005], g[2][1005];

int n, m, cnt, qe = 0;;

const ll maxn = 4294967295;

int nxt[4][1005];




int find( int sta )

{

    int l, r, mid, pos = -1;

    l = 1, r = cnt-1;

    while( l <= r ){

        mid = (l+r)>>1;

        if( v[mid] == sta ){

            pos = mid;

            break;

        }

        if( v[mid] > sta ) r = mid-1;

        else l = mid+1;

    }

    return pos;

}//二分法找出需要的状态




void dfs( int x, int y, int maxn )

{

    int sta, i, j;

    if( x > y ){

        sta = a[1];

        for( i = 2; i <= m; i ++ ){

            sta = sta << 3;

            sta = sta|a[i];

        }

        v[cnt++] = sta;

        return;

    }

    for( a[x] = 1; a[x] <= maxn; a[x] ++ ){

        if( a[x] == maxn )dfs( x+1, m, maxn+1 );

        else dfs( x+1, m, maxn );

    }

}//最小表示法处理出所有的点







int modify( int x, int id )

{

    int b[10], i, j, k, r[10], ans = 0;

    int vis[10];

    memset( vis, 0, sizeof(vis) );

    k = x;

    for( i = m; i > 0 ; i -- ){

        b[i] = k & 7;

        if( id == 1 && b[i] == 1 ){

            b[i] = a[m];

        }

        k >>= 3;

    }

    for( i = 1, j = 1; i <= m; i ++ ){

        if( vis[b[i]] == 0 ){

            r[i] = j;

            vis[b[i]] = j;

            j ++;

        }

        else{

            r[i] = vis[b[i]];

        }

    }

    ans = r[1];

    for( i = 2; i <= m; i ++ ){

        ans <<= 3;

        ans = ans | r[i];

    }

    return ans;

}//将新的点加入原状态




void intit()

{

    int i, j;

    scanf("%d %d", &n, &m);

    for( int i = 1; i <= n; i ++ )

        for( int j = 2; j <= m; j ++ )

        scanf("%d", &left[i][j]);

    for( int i = 2; i <= n; i ++ )

        for( int j = 1; j <= m; j ++ )

        scanf("%d", &up[i][j]);

    cnt = 1;

    memset( a, 0, sizeof(a) );

    dfs( 1, m, 1 );

    sort( v+1, v+cnt );

    for( i = 1; i < cnt; i ++ ){

        int st = v[i], nk, pos;

        for( int l = m, tmp = st; l >= 1;  l -- ){

                a[l] = tmp & 7;

                tmp >>= 3;

        }

        nk = modify( ( (st << 3) | a[m] ) & ( ( 1 << (3*m) ) - 1 ), 1 );

        pos = find(nk);

        nxt[0][i] = pos;

        nk = modify( ( (st << 3) | a[m] ) & ( ( 1 << (3*m) ) - 1 ), -1 );

        pos = find(nk);

        nxt[1][i] = pos;

        nk = modify( ( (st << 3) | 1 ) & ( ( 1 << (3*m) ) - 1 ), -1 );

        pos = find(nk);

        nxt[2][i] = pos;

        nk = modify( ( (st << 3) | 0 ) & ( ( 1 << (3*m) ) - 1 ), -1 );

        pos = find(nk);

        nxt[3][i] = pos;

    }//预处理所有状态转移来加速

}




void solve()

{

    int now = 0, bef = 1, i, j, ok, p, q;

    memset( g, 0, sizeof(g) );

    for( int k = 0; k < 1000; k ++ )f[now][k] = f[bef][k] = maxn;

    f[now][1] = 0; g[now][1] = 1;

    for( i = 1; i <= n; i ++ )

    for( j = 1; j <= m; j ++ ){

        if( i == 1 && j == 1 )continue;

        swap( now, bef );

        memset( g[now], 0, sizeof(g[now]) );

        for( int k = 0; k < 1000; k ++ )f[now][k] = maxn;

        for( p = 1; p < cnt; p ++ ){

            int st = v[p];

            if( f[bef][p] == maxn )continue;

            ok = 0;

            for( int l = 1, tmp = st; l < m; l ++ ){

                if( (tmp & 7) == 1 ){

                    ok = 1;

                }

                tmp >>= 3;

            }

            for( int l = m, tmp = st; l >= 1;  l -- ){

                a[l] = tmp & 7;

                tmp >>= 3;

            }

            if( i > 1 && j > 1 && a[1] != a[m] ){//确保不成环

                int tmp = f[bef][p]+left[i][j]+up[i][j];

                int cu = nxt[0][p];

                if( tmp == f[now][cu] ){

                    g[now][cu] = ( g[now][cu] + g[bef][p] * 3 ) % mod;

                }

                else if( tmp < f[now][cu] ){

                    f[now][cu] = tmp;

                    g[now][cu] = g[bef][p]*3%mod;

                }

            }

            if( j > 1 && ok ){

                int tmp = f[bef][p]+left[i][j];

                int cu = nxt[1][p];

                if( tmp == f[now][cu] ){

                    g[now][cu] = ( g[now][cu] + g[bef][p] * 2 ) % mod;

                }

                else if( tmp < f[now][cu] ){

                    f[now][cu] = tmp;

                    g[now][cu] = g[bef][p]*2%mod;

                }

            }

            if( i > 1 ){

                int tmp = f[bef][p]+up[i][j];

                int cu = nxt[2][p];

                if( tmp == f[now][cu] ){

                    g[now][cu] = ( g[now][cu] + g[bef][p] * 2 ) % mod;

                }

                else if( tmp < f[now][cu] ){

                    f[now][cu] = tmp;

                    g[now][cu] = g[bef][p]*2%mod;

                }

            }

            if( ok ){

                int tmp = f[bef][p];

                int cu = nxt[3][p];

                if( tmp == f[now][cu] ){

                    g[now][cu] = ( g[now][cu] + g[bef][p] ) % mod;

                }

                else if( tmp < f[now][cu] ){

                    f[now][cu] = tmp;

                    g[now][cu] = g[bef][p]%mod;

                }

            }//四种更新

        }

//        for( int k = 1; k < cnt; k ++ ){

//            printf("%d %d %d %I64d %I64d\n", i, j, k, f[now][k], g[now][k]);

//        }

    }

    printf("Case #%d: %I64d %I64d\n", ++qe, f[now][1], g[now][1] );

}







int main()

{

    int T;

    //freopen("out.txt", "w", stdout);

    scanf("%d", &T);

    while( T -- ){

        intit();

        solve();

    }



}















你可能感兴趣的:(生成树计数)