您的位置:首页 > Web前端 > JavaScript

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

2017-12-16 13:59 375 查看
把集合看成一个n位二进制数,第i位为1表示集合中包含i,否则第i位为0表示集合中不包含i。可以想到,如果把二进制数的每一位拆开考虑,那么变成如下模型:

在一个边长为k的三角形内,共k(k+1)2个位置,对于一个位置(i,j),如果(i−1,j)和(i,j−1)都填充了1(如果不存在位置(i−1,j)或(i,j−1)则当作填充1处理),那么(i,j)可填充0或1,否则(i,j)只能填充0。求填充这个三角形的方案数。

可以得到,最后结果等于上面问题结果的n次幂。

对于这个问题,考虑一个DP的做法,设f[i]为到了第i行的方案数。

转移的方法是枚举j从0到i−1,即枚举第i行填充1的个数,最后加上1(前i行全部填充1)

可以得到,第i行中被填充为1的位置是此行的前j个位置。如果第i行的前j个值已经被确定为1,那么后i−j个值也被确定为0。以此类推,前i行的前j列也被定为1,这样只剩下一个边长为i−j−1的三角形没有被填充,并且这个三角形填充的数字不受已填充的数字的影响。所以得到转移:

f[i]=1+∑i−1j=0f[i−j−1]

也就是f[i]=1+∑i−1j=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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: