1. 程式人生 > >CF895C Square Subsets (組合數+狀壓DP+簡單數論)

CF895C Square Subsets (組合數+狀壓DP+簡單數論)

序列 -a 進一步 set cst 數論 scan mes -m

題目大意:給你一個序列,你可以在序列中任選一個子序列,求子序列每一項的積是一個平方數的方案數。

1<=a[i]<=70

因為任何一個大於2的數都可以表示成幾個質數的冪的乘積

所以我們預處理70以內的質數,把它作為二進制狀壓的狀態,每個在序列中出現數Hash一下,組合數推一下

所以把奇次冪的狀態表示為1,偶次冪的狀態就是0,比如6就是11,42就是1011

而平方數的每個質因子的指數都是偶數,所以最終結果的狀態就是0000000...

轉移的過程,兩個數的乘積,就是這兩個數的質因子二進制的狀態的合並,即異或(xor)運算

卡常很惡心,懶得進一步優化了

好吧據說可以推出結論,這個組合數加起來其實是2的冪次

 1
#include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define N 100100 5 #define M 75 6 #define mod 1000000007 7 #define C(m,n) (((fac[n]*inv[m])%mod*inv[n-m])%mod) 8 #define ll long long 9 using namespace std; 10 11 int xx,n,now,lst; 12 int hx[M]; 13 ll x,y,t; 14 ll inv[N],fac[N],f[2
][(1<<19)+100]; 15 int pr[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67}; 16 int gc() 17 { 18 int rett=0,fh=1;char c=getchar(); 19 while(c<0||c>9) {if(c==-)fh=-1; c=getchar();} 20 while(c>=0&&c<=9) {rett=rett*10+c-0;c=getchar();} 21 return rett*fh;
22 } 23 void exgcd(ll a,ll b) 24 { 25 if(b==0) {x=1,y=0;} 26 else {exgcd(b,a%b);t=x;x=y;y=t-a/b*y;} 27 } 28 void get_inv() 29 { 30 inv[0]=inv[1]=1,fac[0]=fac[1]=1; 31 for(ll i=2;i<=n;i++) 32 { 33 exgcd(i,mod); 34 fac[i]=(fac[i-1]*i)%mod; 35 x=(x%mod+mod)%mod; 36 inv[i]=(inv[i-1]*x)%mod; 37 } 38 } 39 40 int main() 41 { 42 //freopen("aa.in","r",stdin); 43 scanf("%d",&n); 44 for(int i=1;i<=n;i++) xx=gc(),hx[xx]++; 45 get_inv(); 46 now=1,lst=0,f[0][0]=1; 47 for(int i=1;i<=70;i++) 48 { 49 if(!hx[i]) continue; 50 int s=0,x=i; 51 for(int j=0;j<19;j++) 52 { 53 while(x%pr[j]==0) 54 { 55 s^=(1<<j); 56 x/=pr[j]; 57 } 58 } 59 for(int p=0;p<(1<<19);p++) 60 { 61 if(!f[lst][p]) continue; 62 for(int j=0;j<=hx[i];j++) 63 { 64 if(j&1) 65 { 66 f[now][p^s]+=(f[lst][p]*C(j,hx[i]))%mod; 67 f[now][p^s]%=mod; 68 }else{ 69 f[now][p]+=(f[lst][p]*C(j,hx[i]))%mod; 70 f[now][p]%=mod; 71 } 72 } 73 f[lst][p]=0; 74 } 75 swap(now,lst); 76 } 77 printf("%I64d\n",f[lst][0]-1); 78 return 0; 79 } 80 81 82

CF895C Square Subsets (組合數+狀壓DP+簡單數論)