Link
Description
给出长为 (n) 的数列 ({a_n}),选出一个长度大于二的子序列,使得
[prod_{i=2}^K inom{b_{i-1}}{b_i} mod 2=1
]
求方案数。
Solution
对组合数取模,容易想到卢卡斯定理,条件就转化为在二进制下,(b_{i-1}) 的每一位都要大于等于 (b_{i}),那么利用集合思想,就能推出 (b_{i-1}&b_i=b_i)。也就是说,满足这个条件就能在 (b_{i-1}) 后面接上一个 (b_i)。容易想到状态 (dp_{i}) 表示以 (a_i) 结尾的序列个数。转移显然。
[dp_{i}=sum_{a_{j}&a_{i}=a_i\ quad j<i} dp_{j}+1
]
那就能得到一个 (O(n^2)) 的 dp,期望得分 70。
再次观察,发现这题的值域很特殊,最大不过二十万。这就提示我们二进制分解下最多有 17 位。题目保证了 (a_i) 不同,那么一个 (a_i) 就唯一对应一个 (i),所以可以直接枚举 (a_i) 补集的子集再或上 (a_i),来枚举 (a_j) ,在判断一下 (j) 是不是在 (i) 前面即可。
因为 (a_i) 不同,所以最坏复杂度就是
[sum_{i=0}^{log a_i} inom{log a_i}{i} 2^i=(2+1)^{log a_i}=3^{log a_i}
]
即 (O(3^{log a_i}))
#include<stdio.h>
#define Mod 1000000007
#define N 240007
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
int n,a[N],dp[N],pos[N];
int main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),pos[a[i]]=i;
for(int i=n-1;i;i--){
for(int j=a[i]&(a[i]-1);j;j=(j-1)&a[i])
if(pos[j]>i) dp[i]=(dp[i]+dp[pos[j]]+1)%Mod;
}
int ans=0;
for(int i=1;i<=n;i++) ans=(ans+dp[i])%Mod;
printf("%d",ans);
}