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; }