[CTSC2017]吉夫特
Link
Description
给出长为 \(n\) 的数列 \(\{a_n\}\),选出一个长度大于二的子序列,使得
\[\prod_{i=2}^K \binom{b_{i-1}}{b_i} \bmod 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} \binom{\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);
}