题目链接
首先我们看两个子序列不同是指下标不同,然后答案只跟子序列之和有关,发现这题跟序列的位置无关,那我们直接对数值考虑,开桶$c_x$表示有$c_x$个$x$元素。
我们发现,每个元素$a_ile 2 imes 10^5$,而一个优秀的子序列里的元素必须满足不能有相同的二进制位,所以子序列和不会超过$2^{18}$。对于一组数据来说,设最大元素的二进制长度为$d$,那么这个和的最大值就不会超过$2^d$,然后所有子序列元素总和组成的全集就是$m=2^d-1$。接下来可能会出现集合和二进制说法混用的情况,意会一下。
然后我们发现,相同元素和的子序列对答案的贡献是一样的,那我们立马想到可以DP,转移出每种总和的子序列个数,简称方案数。统计答案的时候先线性筛算出欧拉函数来就好了。
还有一个就是,$0$元素对子序列和是没有影响的,也就是可以随意选,那么我们计算方案的时候先不考虑$0$,最后所有方案数乘上一个$2^{c_0}$即可。
接下来考虑怎么DP。
设$f_s$表示选出元素和为$s$的子序列的方案数,那么有初值$f_0=0,;f_{x}=c_x$。
然后自然地写出转移方程:$$f_s=sumlimits_{tsubsetneq s}f_{soplus t} imes c_t$$
这里运用了状压DP枚举子集的方法,枚举$s$只能从真子集关系的数值转移过来,然后$oplus$表示异或,由二项式定理可知转移的时间复杂度是$O(3^d)$。
这里出现了一个问题,那就是算重了。举个例子,子序列${1,2}$,这种转移方法既会计算元素$1$加入子序列${2}$形成的方案,也会计算元素$2$加入子序列${1}$形成的方案。因此对于一个长度为$i$的子序列,会算出$i!$个重复的方案。呃,注意我们定义的要求的“方案数”,是指“某种总和的优秀子序列个数”,而不是“组成某种总和的优秀子序列个数的路径计数”。别搞错了,前者才是跟答案直接挂钩的。
有两种解决方法。
第一种是DP再加一维。设$f_{i,s}$表示选了$i$个元素,元素和为$s$的子序列的方案数,其他都差不多,然后对于相同元素和的子序列方案数有$g_s=sumlimits_{i=1}^{min(d,s)} dfrac{f_{i,s}}{i!}$,但是这样时间复杂度就变成了$O(3^dd)$,期望得分60分。
第二种就是找出别的转移方法。一般来说是考虑每次加入最大的数,因为我们更新$f_s$是从小到大枚举的,别乱套了。思考一下,发现新加入的数如果是最大的,一定有比当前集合的最高二进制位更大的二进制位,那么它的值就会大于当前集合。欧了,取出$complement_m s$的所有大于$s$的子集$t$加入即可。因为是刷表法,乱写一个式子:
$$f_s imes sumlimits_{tsubsetneq complement_m s,;t>s}c_t xrightarrow{+}f_{soplus t}$$
感觉说完了。呃,还是写一下快读吧,养成习惯,这输入已经到了$10^6$级别,影响还是挺大的。还有register优化之类的……
代码(100分):

#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define _1 first #define _2 second using namespace std; typedef unsigned long long LL; #define RI RG int const int N=1e6; const int M=1<<18; const LL mod=1e9+7; IL void read(RI &x){ x=0; RG char ch=getchar(); while(!isdigit(ch)) ch=getchar(); for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0'; } int n,m,c[M+3]; IL void init(){ read(n); RI maxx=0; for(RI i=1,x;i<=n;i++){ read(x); c[x]++; maxx=max(maxx,x); } for(m=1;m<=maxx;m<<=1); } LL f[M+3]; int k,lis[M+3]; bool prm[M+3]; int phi[M+3]; IL void getphi(){ phi[1]=1; for(RI i=2;i<=m;i++){ if(!prm[i]){ lis[++k]=i; phi[i]=i-1; } for(RI j=1;j<=k&&i*lis[j]<=m;j++){ prm[i*lis[j]]=true; if(i%lis[j]==0) phi[i*lis[j]]=phi[i]*lis[j]; else phi[i*lis[j]]=phi[i]*(lis[j]-1); } } } IL LL qpow(LL a,LL b){ LL ans=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ans=ans*a%mod; return ans; } int main(){ init(); f[0]=qpow(2,c[0]); for(RI s=1;s<m;s++) f[s]=c[s]; for(RI s=1;s<m;s++) if(f[s]){ for(RI x=s^(m-1),t=x;t>s;t=(t-1)&x) if(c[t]) f[s^t]=(f[s^t]+f[s]*c[t]%mod)%mod; f[s]=f[s]*f[0]%mod; } getphi(); RG LL ans=0; for(RI i=0;i<m;i++) if(f[i]) ans=(ans+f[i]*phi[i+1]%mod)%mod; printf("%lld",ans); return 0; }