[NOI Online #3 提高组] 优秀子序列

这个题怎么不直接取名

集合幂级数$\text{exp}$

优秀的子序列中任意两个元素01位无交,这是一个标准的子集卷积形式

$\varphi$的计算显然与$a_i$的卷积独立,可以线性筛/埃氏筛

暴力

可以暴力$3^{18}$过,枚举时为了避免重复可以通过强制枚举的数包含最高位的1

注意$a_i=0$要特殊处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
bool Mbe;
const int N=1<<18,P=1e9+7;

int n,cnt0=1;
ll qpow(ll x,ll k=P-2){
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}
int Phi[N+1],notpri[N+1];
int F[N],C[N],cnt[N];

int main(){
Phi[1]=1;
rep(i,2,N) if(!notpri[i]) {
Phi[i]=i-1;
for(int j=i+i;j<=N;j+=i) {
notpri[j]=1;
if(!Phi[j]) Phi[j]=j;
Phi[j]=Phi[j]/i*(i-1);
}
}
n=rd();
rep(i,1,N-1) cnt[i]=cnt[i&(i-1)]+1;
F[0]=1;
rep(i,1,n) {
int x=rd();
if(!x) F[0]*=2,Mod1(F[0]);
else C[x]++;
}
int ans=0;
rep(S,0,N-1) {
if(S) for(int T=S;_builtin_clz(S)==__builtin_clz(T);T=(T-1)&S) F[S]=(F[S]+1ll*F[S^T]*C[T])%P;
ans=(ans+1ll*F[S]*Phi[S+1])%P;
}
printf("%d\n",ans);
}

集合幂级数

就是直接套集合幂计数的$\text{exp}$

同样要特殊处理$a_i=0$的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const int N=1<<18,P=1e9+7;
int F[N][19],Inv[20];
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char buf[200000],*p1,*p2;
#define getchar() (((p1==p2)&&(p2=(p1=buf)+fread(buf,1,200000,stdin))),*p1++)
char IO;
int rd(){
int s=0; static char c;
while(c=getchar(),c<48);
do s=(s<<1)+(s<<3)+(c^'0');
while(c=getchar(),c>47);
return s;
}
bool Mbe;

int n,m,cnt0=1,U;
ll qpow(ll x,ll k=P-2){
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}
int Phi[N+1],notpri[N+1],pri[N/4],pc;

void Exp(int *a){
static int b[N];
rep(i,1,n) b[i-1]=1ll*a[i]*i%P;
rep(i,0,n-1) {
int s=b[i];
rep(j,1,i) s=(s+1ll*a[j]*b[i-j])%P;
a[i+1]=1ll*s*Inv[i+1]%P;
}
}

int main(){
Inv[0]=Inv[1]=1;
rep(i,2,18) Inv[i]=1ll*(P-P/i)*Inv[P%i]%P;
n=rd();
rep(i,1,n) {
int x=rd(); cmax(U,x);
if(!x) cnt0*=2,Mod1(cnt0);
else F[x][__builtin_popcount(x)]++;
}
Phi[1]=1;
for(n=1;(1<<n)<=U;)n++;
m=1<<n;
rep(i,2,m) {
if(!notpri[i]) pri[++pc]=i,Phi[i]=i-1;
for(int j=1;j<=pc && 1ll*i*pri[j]<=m;++j){
notpri[i*pri[j]]=1;
if(i%pri[j]==0) {
Phi[i*pri[j]]=Phi[i]*pri[j];
break;
}
Phi[i*pri[j]]=Phi[i]*(pri[j]-1);
}
}
for(int i=1;i<m;i<<=1) for(int l=0;l<m;l+=i*2) for(int j=l;j<l+i;++j) rep(k,1,n) F[j+i][k]+=F[j][k],Mod1(F[j+i][k]);
rep(i,0,m-1) Exp(F[i]);
for(int i=1;i<m;i<<=1) for(int l=0;l<m;l+=i*2) for(int j=l;j<l+i;++j) rep(k,1,n) F[j+i][k]-=F[j][k],Mod2(F[j+i][k]);
int ans=0;
rep(S,1,m-1) ans=(ans+1ll*F[S][__builtin_popcount(S)]*Phi[S+1])%P;
ans=1ll*(ans+1)*cnt0%P;
printf("%d\n",ans);
}