hdu4921 Map(状压统计)

hdu4921 Map(状压统计)

题意:有10条长度不超过1000链,链上的节点有权值。我们从这些节点中选出一些节点来,若要选节点u,则u的前继都得被选进去。对于某一种选定的情况,我们能获得的权值为,选定的节点的权值和,以及一些附加值。附加值的求法为,对于每条链的同一深度的点,若选定的点的个数超过1,那么会得到的附加值为(si*xi/ci),其中si表示该层选中的点的权值和,xi为该层选中的点的个数,ci为该层的总点数。问,对于所有的选择情况,能得到的权值期望会是多少?

解法:很直观的一点,期望值=所有情况的权值和/所有的情况数。情况数很好算,所有的链长+1求乘积即可(要减去什么都不选的一种情况)。而所有的情况的权值和,我们这样算:因为同一层之间的点,选的个数会对该层产生一个影响,所以我们一层一层考虑。总共只有10条链,故考虑任何一层上的点,选或者不选的状态只有2^10种,那么我们枚举这2^10个状态,对于某一个状态,我们可以暴力算出能得到的点权和以及附加值,但是对于这个状态,它会出现在很多种选择情况当中。比如我们有两条链,链长都是3,当前考虑的是第二层的10状态(二进制1表示选,0表示不选),那么,当链1分别取前2个,前3个,链2分别取前0个,前1个。这四种都会包含第二层10的状态,因此,这个状态会在4种选择情况中贡献给权值总和,故而要将求出的点权和+附加值乘上情况数再累加到权值总和当中,每一层都考虑完了之后,问题得解。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std ;

int son[11111] ;
int chain[10][1111] , a[11111] ;
int vis[11111] , du[11111] , len[11] ;

double dfs ( int u , int step , int c ) {
    chain[c][step] = a[u] ;
    if ( son[u] == -1 ) return step + 2 ;
    return dfs ( son[u] , step + 1 , c ) ;
}

int main () {
    int n , m ;
    int T ;
    scanf ( "%d" , &T ) ;
    while ( T -- ) {
        scanf ( "%d%d" , &n , &m ) ;
        memset ( chain , 0 , sizeof ( chain ) ) ;
        memset ( du , 0 , sizeof ( du ) ) ;
        memset ( son , -1 , sizeof ( son ) ) ;
        for ( int i = 0 ; i < n ; i ++ )
            scanf ( "%d" , &a[i] ) ;
        for ( int i = 1 ; i <= m ; i ++ ) {
            int a , b ;
            scanf ( "%d%d" , &a , &b ) ;
            son[a] = b ;
            du[b] ++ ;
        }
        double sum = 1 , ans = 0 ;
        int tot = 0 ;
        for ( int i = 0 ; i < n ; i ++ ) {
            if ( du[i] == 0 ) {
                len[tot] = dfs ( i , 0 , tot ) ;
                sum *= len[tot] ;
                len[tot] -- ;
                tot ++ ;
            }
        }
        sum -= 1 ;
   //     printf ( "sum = %f\n" , sum ) ;
        for ( int i = 0 ; i < 1000 ; i ++ ) {
            for ( int j = 0 ; j < 1 << tot ; j ++ ) {
                int flag = 0 ;
                double add = 0 , p = 1 , x = 0 , cnt = 0 ;
                for ( int k = 0 ; k < tot ; k ++ ) {
                    if ( chain[k][i] ) cnt ++ ;
                    if ( j & (1<<k) ) {
                        if ( chain[k][i] == 0 ) {
                            flag = 1 ;
                            break ;
                        }
                        add += chain[k][i] ;
                        x ++ ;
                        p *= len[k] - i ;
                    }
                    else {
                        p *= min ( len[k] + 1 , i + 1 ) ;
                    }
                }
                if ( flag ) continue ;
                double fuck = ( add + (x>1?(add*x/cnt):0) ) * p ;
        //        printf ( "i = %d , j = %d , fuck = %f\n" , i , j , fuck ) ;
        //        printf ( "add = %lf , p = %lf , x = %lf , cnt = %lf\n" , add , p , x , cnt ) ;
                ans += fuck ;
            }
        }
        printf ( "%.3f\n" , ans / sum ) ;
    }
    return 0 ;
}

hdu4921 Map(状压统计)

时间: 2024-12-18 11:19:32

hdu4921 Map(状压统计)的相关文章

hdu 4909 String (map + 状压)

题目大意: 给定一个可能含'?'的字符串.然后问这个字符串有多少个子串是含有所有的字符都只出现两次. 其中'?' 可以被替换成任意字符,也可以被remove... 思路分析: 这是bestcoder的round #3的第三题. 这道题的做法和 4908 的做法差不多. 我们把 '?' 左右两边的状态分别处理出来. 然后用map 计数.然后枚举左边的状态.同时枚举? 对应的字符. 然后去寻找右边对应的状态,此时 ans 就可以加上答案. 注意的是要考虑到以 ? 结尾的情况.所以在 ? 的状态要和上

SDUT 3568 Rock Paper Scissors 状压统计

就是改成把一个字符串改成三进制状压,然后分成前5位,后5位统计, 然后直接统计 f[i][j][k]代表,后5局状压为k的,前5局比和j状态比输了5局的有多少个人 复杂度是O(T*30000*25*m)m比较小,也就最多几十吧,将将过 #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cstdlib> #include &

HDU 4909 String 统计+状压

因为连续异或满足区间减法性质,所以可以状压之后用异或来判断是否为符合条件的单词并且存储次数 一开始用map,一直超时.虽然直接用开1<<26的数组内存存的下,但是memset的时间肯定会超,但是只要在每次循环之后把加过的值减掉就可以绕过memset了. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits>

CH 2101 - 可达性统计 - [BFS拓扑排序+bitset状压]

题目链接:传送门 描述 给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量.N,M≤30000. 输入格式 第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边. 输出格式 共N行,表示每个点能够到达的点的数量. 样例输入 10 10 3 8 2 3 2 5 5 9 5 9 2 3 3 9 4 8 2 10 4 9 样例输出 1 6 3 3 2 1 1 1 1 1 题解: 首先,如果用 $f(x)$ 代表从点 $x$ 出发所能到达的所有点的集合,应有

HDU 4352 XHXJ&#39;s LIS 数位DP + 状压

由LIS的nlogn解法 可以得出最后统计数组中数的个数即为LIS的长度 这样就可以状压了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <c

ZOJ3802 Easy 2048 Again (状压DP)

ZOJ Monthly, August 2014 E题 ZOJ月赛 2014年8月 E题 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5334 Easy 2048 Again Time Limit: 2 Seconds      Memory Limit: 65536 KB Dark_sun knows that on a single-track road (which means once he passed this

[ An Ac a Day ^_^ ] POJ 3254 Corn Fields 状压dp

题意: 有一块n*m的土地 0代表不肥沃不可以放牛 1代表肥沃可以放牛 且相邻的草地不能同时放牛 问最多有多少种放牛的方法并对1e8取模 思路: 典型的状压dp 能状态压缩 能状态转移 能状态压缩的题的特点就是只有两种状态 所以用0 1表示两种状态 用位运算判断是否符合条件 然后将前一行的合理状态转移到后一行 最后统计最后一行的状态 dp[i][j]代表第i行以第j种状态放牛时有多少种不同的状态 (c++的语言特性是 封装 继承 多态~) 1 /* ***********************

【POJ 3254】 Corn Fields(状压DP)

[POJ 3254] Corn Fields(状压DP) Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10891   Accepted: 5705 Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parce

CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)

问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次. 2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前. 3. 最高位数字不为0. 因此,符合我们定义的最小的有趣的数是2013.除此以外,4位的有趣的数还有两个:2031和2301. 请计算恰好有n位的有趣的数的个数.由于答案可能非常大,只需要输出答案除以1000000007的余数. 输入格式 输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000). 输