某种密码(password.*)
关于某种密码有如下描述:某种密码的原文A是由N个数字组成,而密文B是一个长度为N的01数串,原文和密文的关联在于一个钥匙码KEY。若KEY=∑▒〖Ai*Bi〗,则密文就是原文的一组合法密码。
现在有原文和钥匙码,请编一个程序来帮助他统计到底有多少个符合条件的密文。
【输入数据】
第一行两个数N,KEY,意义同题目描述;
第二行N个数表示原文A,意义同题目描述。
【输出数据】
一个数ANS,表示对于原文A和KEY,有多少组可行的密文B。
【输入样例】
3 2
1 1 2
【输出样例】
2
【样例说明】
密文110,1*1+1*1+0*2=2
密文001,0*1+0*1+1*2=2
一共两组可行的密文。
【数据约定】
60%数据满足N<=25
100%数据满足N<=40,-maxlongint<=∑▒Ai<=maxlongint
题意是给定n个数,要求做01背包之后和为key的方案数
首先n<=25的直接2^n爆搜就好了
当n<=40的时候,有一种做法是类似“方程的解数”那题的前后分开暴力
假设b[]是一个满足Σa[i]*b[i]=key的01序列
首先,b[]数组必须刚好n个数
显然b[]数组最多有2^40种不同的排列,直接爆搜会T
但是我们把b[]的前n/2个元素暴力枚举出来,最坏情况下是2^20,用hash存起来
然后再暴力枚举后n-n/2个,最坏也是2^20,统计答案的时候只要找前面跟它刚好匹配的方案数就好了
比如找到一种和为a的方案,只要在哈希表中找前20个中和为key-a的方案数累加就好了
这样复杂度降到(n/2)*2^(n/2)
在考场上a[i]开了long long但是读入没有用%lld就只有70……蛋疼
#include<cstdio> #include<iostream> #include<algorithm> #define LL long long #define mod 1000007 #define deliver n/2 using namespace std; struct hashing{ LL next,rep; LL v; }hash[5000000]; int head[mod]; int n,m,cnt; LL ans; LL a[100]; inline void ins(LL u,LL w) { hash[++cnt].v=w; hash[cnt].rep=1; hash[cnt].next=head[u]; head[u]=cnt; } inline void insert(LL u,LL w) { LL s=w%mod;while(s<0)s+=mod; for(int i=head[s];i;i=hash[i].next) if (hash[i].v==w) { hash[i].rep++; return; } ins(s,w); } inline int get(int u) { LL w=(LL)(m-u); LL s=w%mod;while (s<0)s+=mod; for (int i=head[s];i;i=hash[i].next) if (hash[i].v==w)return i; return -1; } int main() { freopen("password.in","r",stdin); freopen("password.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++)scanf("%lld",&a[i]); sort(a+1,a+n+1); for(int i=0;i<=(1<<deliver)-1;i++) { LL tot=0; for (int j=1;j<=deliver;j++) if (i & (1<<(j-1)))tot+=a[j]; insert(tot,tot); } for (int i=0;i<=(1<<(n-deliver))-1;i++) { LL tot=0; for (int j=1;j<=n-deliver;j++) if (i & (1<<(j-1)))tot+=a[j+deliver]; int fnd=get(tot); if (fnd!=-1)ans+=hash[fnd].rep; } printf("%lld ",ans); }