CF 297 div2 E. Anya and Cubes (hash+dfs)

题目:http://codeforces.com/contest/525/problem/E

题意:输入N,K,S。N代表接下来要输入的数字序列{a1,a2...an}的个数,K代表有K次机会marks使其中一个数ai变成ai的阶乘,求选x个数(1<=x<=N)使用y次marks(0<=y<=K)使它们的和为S的方案数。

分析:枚举一半的数字,将结果存进hash表,然后枚举后面一半的数字在hash表里面查询。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <string>

using namespace std;
typedef long long LL;
#define MOD 1000019
#define MAXN 3000000
LL a[10000];

LL fac[]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,
		1307674368000,20922789888000,355687428096000,6402373705728000};

struct node
{
	LL value;
	LL use;
	LL count;
	int next;
};

struct Hash
{
	LL Table[MOD],cnt;
	node List[MAXN];
	void Clear()
	{
		cnt=0ll;
		for(int i=0;i<MOD;i++)
			Table[i]=-1ll;
	}
	void Insert(LL v,LL use)
	{
		LL hash=v%MOD;
		LL temp=Table[hash];
		while(temp!=-1)
		{
			if(List[temp].value==v && List[temp].use==use)
			{
				List[temp].count++;
				return ;
			}
			temp=List[temp].next;
		}
		List[cnt].value=v;
		List[cnt].use=use;
		List[cnt].count=1;
		List[cnt].next=Table[hash];
		Table[hash]=cnt;
		cnt++;
	}
	LL Search(LL v,LL use)
	{
		LL hash=v%MOD;
		hash=Table[hash];
		LL ret=0; 
		while(hash!=-1)
		{
			if(List[hash].value==v && List[hash].use<=use)
				ret+=List[hash].count;
			hash=List[hash].next;
		}
		return ret;
	}
}H;

LL S,K,N,ans;

void dfs1(LL cur_n,LL n,LL v,LL u)
{
	if(cur_n>n+1 || u>K || v>S)
		return ;
	if(cur_n==n+1)
	{
		H.Insert(v,u);
		return ;
	}
	if(a[cur_n]<19)
		dfs1(cur_n+1,n,v+fac[a[cur_n]],u+1);
	dfs1(cur_n+1,n,v+a[cur_n],u);
	dfs1(cur_n+1,n,v,u);
}

void dfs2(LL cur_n,LL n,LL v,LL u)
{
	if(cur_n>n+1 || u>K || v>S)
		return ;
	if(cur_n==n+1)
	{
		ans+=H.Search(S-v,K-u);
		return ;
	}
	if(a[cur_n]<19)
		dfs2(cur_n+1,n,v+fac[a[cur_n]],u+1);
	dfs2(cur_n+1,n,v+a[cur_n],u);
	dfs2(cur_n+1,n,v,u);
}

int main()
{
	LL i,j;
	cin>>N>>K>>S;
	for(i=0;i<N;i++)
		cin>>a[i];
	ans=0;
	H.Clear();
	dfs1(0ll,N/2-1ll,0ll,0ll);
	dfs2(N/2ll,N-1ll,0ll,0ll);
	cout<<ans<<"\n";
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值