luoguP6570 [NOI Online #3 提高组]优秀子序列 dp
由于集合内的数不能有交集,所以显然是子集 dp 的形式.
但是这道题为了不算重不太能用 FWT 优化,直接暴力 dp 的话是 $O(3^{18})$ 的,洛谷上开 O2 能过.
转移 DP 的时候一定注意是 $many[i] \times f[j]$,而不是 $f[i] \times f[j]$,否则会算重.
很多细节在想题的时候一定要注意到!
code:
#include <bits/stdc++.h>
#define N 1000007
#define ll long long
#define mod 1000000007
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,cnt;
int a[N],f[N];
int phi[N],prime[N],vis[N],many[N];
void init()
{
phi[1]=1;
for(int i=2;i<N;++i)
{
if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&prime[j]*i<N;++j)
{
vis[i*prime[j]]=1;
if(i%prime[j]!=0) phi[i*prime[j]]=phi[i]*(prime[j]-1);
else
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
}
int qpow(int x,int y)
{
int tmp=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) tmp=(ll)tmp*x%mod;
return tmp;
}
int main()
{
// setIO("input");
scanf("%d",&n);
int mx=0;
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]),f[a[i]]++,many[a[i]]++;
mx=max(mx,a[i]);
}
mx<<=1;
init();
for(int i=1;i<=mx;++i)
{
int c=(i%2==0)?i/2:i/2+1;
for(int j=i&(i-1);j>=c&&j;j=i&(j-1))
{
f[i]+=(ll)many[j]*f[i^j]%mod;
f[i]>=mod?f[i]-=mod:0;
}
}
for(int i=1;i<=mx;++i)
f[i]=(ll)f[i]*qpow(2,f[0])%mod;
f[0]=qpow(2,f[0]);
int ans=0;
for(int i=0;i<=mx;++i)
if(f[i]) ans+=(ll)phi[i+1]*f[i]%mod,ans>=mod?ans-=mod:0;
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号