description
solution
显然只要选出来的子序列有一个组合数为偶数,最后取模 2 2 2 的结果都会是零
有一个结论:当且仅当n&m=m
时,
C
n
m
C_n^m
Cnm为奇数
所以我们要选的子序列,任意相邻两位中后一位的下标和前一位的下标都要满足上面这个结论,才会是奇数
由此就跟二进制扯上了关系
设 f [ i ] f[i] f[i]表示强制以 a [ i ] a[i] a[i]结尾的合法方案数
- 枚举 i i i,先统计答案 f [ i ] f[i] f[i],再转移后面的 j j j,( 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 , j i,j i,j, f [ i ] = ∑ j < i f [ 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]的子集),再统计答案
结合二进制进行很妙的优化, 2 17 < n < 2 18 2^{17}<n<2^{18} 217<n<218
- 转移:枚举前 9 9 9位的超集,固定后 9 9 9位
- 更新:固定前 9 9 9位,枚举后 9 9 9位的子集
转移和更新交替进行,从而达到最原始的思路实现,也降了时间复杂度
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;
}