[CTSC2017]吉夫特
给定一个长度为\(n(n\leq 211985)\)的序列\(a_1\cdots a_n(1\leq a_i\leq 233333)\),问有多少个长度\(>1\)的不上升子序列\(a_{b_1},a_{b_2},\cdots ,a_{b_m}\)满足:
\[\prod_{i=1}^{m-1} {a_i\choose a_{i+1}}\pmod 2\equiv 1
\]
根据lucas定理,如果把\(a_i\)转化为二进制数,那么\(a_{i+1}\)一定是\(a_i\)的子集。有一个基础的思路是,设\(f_i\)为以\(i\)结尾有多少个子序列。那么当前的\(a_i\)就要从\(a_i\)的超集转移过来。直接做就是枚举\(i\)以前的每个\(a\),暴力转移,复杂度\(n^2\)。
有两种优化,一种是因为超集不好枚举,所以倒着转移,变为枚举子集。复杂度的话就由于\(a_i\leq 2^18\)且互不相等,那假设所有\(0\)~\(2^18-1\)的数都出现了,枚举\(a_i\)中1的个数就是:\(\sum_{i=0}^{18}{18\choose i}2^i=3^{18}\)。
另一种优化能适用于更广泛的情况,复杂度不要求\(a_i\)互不相同。设\(g_{p,q}\)表示前\(9\)位为\(p\),后9位为\(q\)的超集的方案数。假设\(a_i\)前9位和后9位为\(u,v\),那么就枚举\(u\)的超集\(s\),由\(g_{s,q}\)转移过来。更新\(g\)的时候就枚举\(v\)的子集\(s\),转移到\(g_{u,s}\)中去。复杂度\(512n\)
#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
using namespace std;
typedef cn int cint;
il int rd(){
rg int x(0),f(1); rg char c(gc);
while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x*f;
}
cint maxn = 240010, mod = 1e9+7;
int n, mx, f[maxn], g[512][512], ans;
il void add(int &a, int b){if((a+=b)>=mod)a-=mod;}
int main(){
n = rd();
fp(i, 1, n){
rg int a = rd();
mx = max(mx, a);
rg int p = a/512, q = a%512, val = 1;
fp(s, p, 511)if((s&p) == p)add(val, g[s][q]);
add(f[a], val);
fp(s, 0, q)if((s&q) == s)add(g[p][s], val);
}
fp(i, 1, mx)add(ans, f[i]);
add(ans, mod-n);
printf("%d\n", ans);
return 0;
}