您的位置:首页 > 其它

2014.10.4模拟赛【某种密码】

2014-10-04 20:58 375 查看
某种密码(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\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: