BZOJ.2679.Balanced Cow Subsets(meet in the middle)
阿新 • • 發佈:2018-12-04
\(Description\)
給定\(n\)個數\(A_i\)。求它有多少個子集,滿足能被劃分為兩個和相等的集合。
\(n\leq 20,1\leq A_i\leq10^8\)。
\(Solution\)
顯然我們要預處理哪些集合可以被劃分為兩個和相等的集合。每個元素三種狀態,這樣我們就可以得到一個\(O(3^n)\)的做法。。
顯然不行啊,但是和相等這種東西可以折半啊!
將序列分成兩半分開DFS。這樣兩個和相等的集合\(S_1,S_2\)中的元素可能會被分開。設\(a\)為\(S_1\)在前一半中序列的元素的和,\(b\)為\(S_1\)在後一半序列中的元素的和;\(c,d\)
和為\(a-c\)的集合可能有多個,直接\(Hash/map+vector\)存一下有哪些集合就可以了(這題集合可以直接二進位制狀壓)。
複雜度是不是還會被卡到\(O(6^{\frac n2})\)啊。。在SPOJ是T了,但能過BZOJ。
其實合併狀態可以sort後線性合併,就快很多了。(不對啊,感覺複雜度差不多啊==,我hash寫太醜了?)
當然這也不是正解,還有更快的,比如:https://blog.csdn.net/u014609452/article/details/51872702
//7132kb 2584ms #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() #define mod 20000000 typedef long long LL; const int N=61005;//為啥60005在BZOJ上RE啊,自測可過。 int n,mid,cnt,A[23],Enum,H[(1<<20)+5],sum[N],nxt[N]; bool vis[(1<<20)+5]; struct Node { int s,sum; bool operator <(const Node &x)const { return sum<x.sum; } }rs[N]; 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; } inline void AE(int s,int Sum) { sum[++Enum]=Sum, nxt[Enum]=H[s], H[s]=Enum; } void DFS1(int x,int s,int sum) { if(x<0) {AE(s,sum); return;} DFS1(x-1,s,sum), DFS1(x-1,s|(1<<x),sum+A[x]), DFS1(x-1,s|(1<<x),sum-A[x]); } void DFS2(int x,int s,int sum) { if(x==n) {rs[++cnt]=(Node){s,sum}; return;} DFS2(x+1,s,sum), DFS2(x+1,s|(1<<x),sum+A[x]), DFS2(x+1,s|(1<<x),sum-A[x]); } int main() { static int lsum[N]; n=read(),mid=n>>1; for(int i=0; i<n; ++i) A[i]=read(); DFS1(mid-1,0,0), DFS2(mid,0,0); std::sort(rs+1,rs+1+cnt); for(int s=0,l=1<<mid; s<l; ++s) { int t=0; for(int i=H[s]; i; i=nxt[i]) lsum[++t]=sum[i]; std::sort(lsum+1,lsum+1+t); for(int i=1,now=1; i<=cnt; ++i) { while(now<=t && lsum[now]<rs[i].sum) ++now; if(now>t) break; if(lsum[now]==rs[i].sum) vis[rs[i].s|s]=1; } } int ans=0; for(int i=1,l=1<<n; i<l; ++i) ans+=vis[i];//同一個集合會算重啊 printf("%d\n",ans); return 0; }
在SPOJ上T掉的:
//89344kb 7060ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define mod 20000000
typedef long long LL;
const int N=61005;
int n,mid,A[23];
bool vis[(1<<20)+5];
struct Hash_Table
{
int delta,Enum,H[mod+2],nxt[N],s[N]; LL sum[N];//可能衝突 再存一下sum
inline void Insert(int S,LL Sum)
{//注意和可能是負的(加一個mod不就好了==)
int x=(Sum+delta)%mod;
s[++Enum]=S, sum[Enum]=Sum, nxt[Enum]=H[x], H[x]=Enum;
}
inline void Solve(int S,LL Sum)
{
int x=(Sum+delta)%mod;
for(int i=H[x]; i; i=nxt[i])
if(sum[i]==Sum) vis[S|s[i]]=1;
}
}hs;
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;
}
void DFS1(int x,int s,LL sum)
{
if(x==mid) {hs.Insert(s,sum); return;}
DFS1(x+1,s,sum), DFS1(x+1,s|(1<<x),sum+A[x]), DFS1(x+1,s|(1<<x),sum-A[x]);
}
void DFS2(int x,int s,LL sum)
{
if(x==n) {hs.Solve(s,sum); return;}
DFS2(x+1,s,sum), DFS2(x+1,s|(1<<x),sum+A[x]), DFS2(x+1,s|(1<<x),sum-A[x]);
}
int main()
{
n=read(),mid=n>>1; int s=0;
for(int i=0; i<n; ++i) s+=A[i]=read();
hs.delta=s, DFS1(0,0,0), DFS2(mid,0,0);
int ans=0;
for(int i=1,l=1<<n; i<l; ++i) ans+=vis[i];//同一個集合會算重啊
printf("%d\n",ans);
return 0;
}