[NOI Online #3 提高组] 优秀子序列
[NOI Online #3 提高组] 优秀子序列
这个题怎么不直接取名
优秀的子序列中任意两个元素01位无交,这是一个标准的子集卷积形式
\(\varphi\)的计算显然与\(a_i\)的卷积独立,可以线性筛/埃氏筛
暴力
可以暴力\(3^{18}\)过,枚举时为了避免重复可以通过强制枚举的数包含最高位的1
注意\(a_i=0\)要特殊处理
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\)的
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);
}