• hdoj4906 Our happy ending(2014 Multi-University Training Contest 4)


    对于一个定长(size = n)的数列a, 若其存在“位置相关”的子集(含空集)使得该子集所有元素之和为k,那么将数列a计数。

    其中数列a中任一元素a[i]在[0, l]内自由取值。

    数据条件0≤n, k ≤ 20, 0≤ l ≤ 1e9,计数结果对mod = 1e9 + 7取模。

    无论直接计数还是考虑从反面计数都解决不了去重的问题,只能考虑dp。

    枚举数列的长度i和压缩后的状态j,并且记录在该条件下的数列选取方案数dp[i][j]。

    压缩后的状态j表示对于集合{1, 2, ..., min(l, k)}的选取情况。

    其中集合中第i个元素在状态j中当且仅当j的二进制串的第i位为1。

    显然我们有dp[0][0] = 1。

    对于长度为p的数组,第p位可以选取的元素是0,1,2,...,l

    考虑p位选取1,2,...,min(l, k)

    那么对于数组长度为p-1时的任一状态j,在数组后追加元素i后的状态为:

    j1 = j | ((j << i) & ((1 << k) - 1)) | (1 << (i - 1))

    三部分分别表示原状态,原状态每个元素与元素i求和后增加的状态,i本身。 

    于是dp[p][j1] += dp[p - 1][j]。

    而对于p位选取0或者大于min(l, k)的情形,j1 = j。

    因此有dp[p][j] = (l - min(l, k)) * dp[p - 1][j]。

    我们最后只需对dp[n][j],其中j第k位为1的累加即可。

    由于数列每个元素非负,我们最后只关心那些集合存在和为k的子集,

    而那些大于k的元素必然不是构成子集的元素,只有那些小于k的元素才对状态转移有用。

    因此我们可以这样表示状态。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <map>
     5 #include <string>
     6 #include <vector>
     7 #include <set>
     8 #include <cmath>
     9 #include <ctime>
    10 using namespace std;
    11 #define lson (u << 1)
    12 #define rson (u << 1 | 1)
    13 typedef __int64 ll;
    14 const int maxn = 1e6 + 10;
    15 const int maxm = 1050;
    16 const ll mod = 1e9 + 7;
    17 
    18 int n, l, k;
    19 ll dp[(1 << 21) + 10];
    20 int main(){
    21     int T;
    22     scanf("%d", &T);
    23     while(T--){
    24         scanf("%d%d%d", &n, &k, &l);
    25         ll d = abs(l - k);
    26         l = min(l, k);
    27         int s = (1 << k) - 1;
    28         memset(dp, 0, sizeof dp);
    29         dp[0] = 1;
    30         while(n--){
    31             for(int j = s; j >= 0; j--){
    32                 ll tem = dp[j];
    33                 if(!tem) continue;
    34                 for(int p = 1; p <= l; p++){
    35                     int nex = (1 << (p - 1)) | j | ((j << p) & s);
    36                     dp[nex] = (dp[nex] + tem) % mod;
    37                 }
    38                 dp[j] = (tem * (1 + d)) % mod;
    39             }
    40         }
    41         ll ans = 0;
    42         for(int i = s; i >= 0; i--)
    43             if(i & (1 << (k - 1)))
    44                 ans = (ans + dp[i]) % mod;
    45         printf("%I64d
    ",ans);
    46     }
    47     return 0;
    48 }
    View Code
  • 相关阅读:
    spring Cloud中,解决Feign/Ribbon整合Hystrix第一次请求失败的问题?
    spring boot集成mybatis
    feign实现服务间的负载均衡
    如何解决Eureka Server不踢出已关停的节点的问题?
    spring-cloud-ribbon负载均衡
    安装配置php5.4 win2003
    ThinkPHP3.2 G函数代码及 使用方法
    php获取数组第一个值 current()
    checkbox全选,反选,取消选择 jquery
    JavaScript If...Else 语句
  • 原文地址:https://www.cnblogs.com/astoninfer/p/4889972.html
Copyright © 2020-2023  润新知