2014.10.4模拟赛【某种密码】

30 篇文章 1 订阅
3 篇文章 0 订阅

某种密码(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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值