[BZOJ4475][JSOI2015]子集选取(DP+结论)

把集合看成一个 n 位二进制数,第i位为 1 表示集合中包含i,否则第 i 位为0表示集合中不包含 i 。可以想到,如果把二进制数的每一位拆开考虑,那么变成如下模型:
在一个边长为k的三角形内,共 k(k+1)2 个位置,对于一个位置 (i,j) ,如果 (i1,j) (i,j1) 都填充了 1 (如果不存在位置(i1,j) (i,j1) 则当作填充 1 处理),那么(i,j)可填充 0 1,否则 (i,j) 只能填充 0 。求填充这个三角形的方案数。
可以得到,最后结果等于上面问题结果的n次幂。
对于这个问题,考虑一个DP的做法,设 f[i] 为到了第 i 行的方案数。
转移的方法是枚举j 0 i1,即枚举第 i 行填充1的个数,最后加上 1 (前i行全部填充 1
可以得到,第i行中被填充为 1 的位置是此行的前j个位置。如果第 i 行的前j个值已经被确定为 1 ,那么后ij个值也被确定为 0 。以此类推,前i行的前 j 列也被定为1,这样只剩下一个边长为 ij1 的三角形没有被填充,并且这个三角形填充的数字不受已填充的数字的影响。所以得到转移:
f[i]=1+i1j=0f[ij1]
也就是 f[i]=1+i1j=0f[j]
由于 f[0]=1 ,因此归纳得出 f[k]=2k
所以答案 =2nk
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int PYZ = 1e9 + 7;
int n, K;
int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1ll * res * a % PYZ;
        a = 1ll * a * a % PYZ;
        b >>= 1;
    }
    return res;
}
int main() {
    cin >> n >> K;
    cout << qpow(2, 1ll * n * K % (PYZ - 1)) << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值