SPOJ.TLE - Time Limit Exceeded(DP 高維字首和)
阿新 • • 發佈:2018-12-05
\(Description\)
給定長為\(n\)的陣列\(c_i\)和\(m\),求長為\(n\)的序列\(a_i\)個數,滿足:\(c_i\not\mid a_i,\quad a_i\&a_{i+1}=0\)。
\(n\leq 50,m\leq 15,0\leq a_i<2^m,0<c_i\leq 2^m\).
\(Solution\)
DP。限制都是與值有關的,所以令\(f_i\)表示以\(i\)這個數結尾的序列\(a\)的個數。
轉移即\(f_i=\sum_{j,i\&j=0}f_j\)。\(i\&j=0\)需要\(3^n\)列舉補集的子集,但是還可以寫成\(i\&(\sim j)=i\)
所以先把上一次的DP陣列下標反轉,就可以用高維字首和優化列舉超集了。
對於\(c_i\not\mid a_i\)的限制,每次轉移完將下標為\(c_i\)倍數的\(f_i\)置為\(0\)即可。
這樣轉移\(n\)次就可以了。複雜度\(O(nm2^m)\)。
反轉下標的那種寫法好騷啊。。
還有列舉子集的方法表示不知道為什麼對。。:http://www.cnblogs.com/zwfymqz/p/9911351.html
#include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() #define mod 1000000000 #define Add(x,v) (x+=v)>=mod&&(x-=mod) typedef long long LL; const int N=(1<<15)+5; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } int main() { static int f[N],tmp[N]; for(int T=read(); T--; ) { int n=read(),m=read(),lim=(1<<m)-1; memset(f,0,sizeof f); f[0]=1; for(int i=1; i<=n; ++i) { // for(int s=0; s<=lim; ++s) tmp[s^lim]=f[s]; // for(int s=0; s<=lim; ++s) f[s]=tmp[s]; for(int s=0; s<=lim; s+=2) std::swap(f[s],f[s^lim]); for(int j=0; j<m; ++j) for(int s=0; s<=lim; ++s) if(!(s>>j&1)) Add(f[s],f[s|(1<<j)]); int ci=read(); for(int j=0; j<=lim; j+=ci) f[j]=0; } LL ans=0; for(int i=0; i<=lim; ++i) ans+=f[i]; printf("%d\n",(int)(ans%mod)); } return 0; }