[CTSC2017]吉夫特(思维+巧妙)

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]结尾的合法方案数

  1. 枚举 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]
  2. 枚举 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

  1. 转移:枚举前 9 9 9位的超集,固定后 9 9 9
  2. 更新:固定前 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值