zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Socket编程实践(6) --TCPNotes服务器
    2014百度之星预赛(第二场)——Best Financing
    操作和维护经常使用的命令
    HDU 5073 Galaxy(Anshan 2014)(数学推导,贪婪)
    [Angular2] @Ngrx/store and @Ngrx/effects learning note
    [AngularJS Ng-redux] Integrate ngRedux
    [AngularFire2] Update multi collections at the same time with FirebaseRef
    [AngularJS] Write a simple Redux store in AngularJS app
    [AngularJS] Using an AngularJS directive to hide the keyboard on submit
    [Javascript] Validate Data with the Every() Method
  • 原文地址:https://www.cnblogs.com/astoninfer/p/4889972.html
Copyright © 2011-2022 走看看