description
戳我看题目
solution
显然只要选出来的子序列有一个组合数为偶数,最后取模 222 的结果都会是零
有一个结论:当且仅当n&m=m
时,CnmC_n^mCnm为奇数
所以我们要选的子序列,任意相邻两位中后一位的下标和前一位的下标都要满足上面这个结论,才会是奇数
由此就跟二进制扯上了关系
设f[i]f[i]f[i]表示强制以a[i]a[i]a[i]结尾的合法方案数
- 枚举iii,先统计答案f[i]f[i]f[i],再转移后面的jjj,(a[j]a[j]a[j]是a[i]a[i]a[i]的超集),f[j]+=f[i]f[j]+=f[i]f[j]+=f[i]
- 枚举i,ji,ji,j,f[i]=∑j<if[j]f[i]=\sum_{j<i}f[j]f[i]=∑j<if[j](满足a[j]a[j]a[j]是a[i]a[i]a[i]的子集),再统计答案
结合二进制进行很妙的优化,217<n<2182^{17}<n<2^{18}217<n<218
- 转移:枚举前999位的超集,固定后999位
- 更新:固定前999位,枚举后999位的子集
转移和更新交替进行,从而达到最原始的思路实现,也降了时间复杂度
code
#include <cstdio>
#define mod 1000000007
#define lim ( 1 << 9 ) - 1
int n;
long long ans;
long long f[1 << 18];int main() {scanf( "%d", &n );for( int i = 1, x;i <= n;i ++ ) {scanf( "%d", &x );int l = x & lim, r = x >> 9, t = 0;for( int j = r;j <= lim;j = ( j + 1 ) | r ) //转移:枚举前9位的超集t = ( t + f[( j << 9 ) | l] ) % mod;ans = ( ans + t ) % mod;t ++; //a[i]本身算一个r <<= 9; for( int j = l;j;j = ( j - 1 ) & l ) //更新:枚举后9位的子集f[j | r] = ( f[j | r] + t ) % mod;f[r] = ( f[r] + t ) % mod; //0|r也算合法方案 特殊处理}printf( "%lld", ans );return 0;
}