codeforces 525 E Anya and Cubes 中途相遇法

codeforces 525 E Anya and Cubes 中途相遇法

题意:

给出n个数a1,a2,...,an,要求从中选出一些数,可以把其中最多k个变成它自己的阶乘,然后选出的数求和,问最后和等于s的选法有多少种。

限制:

1 <= n <= 25; 0 <= k <= n; 1<= s <= 1e16; 1 <= ai <= 1e9

思路:

一般数据量20~30都会考虑中途相遇法,就是折半暴力。

ps:用三进制暴力会比直接深搜多一个常数10,因为三进制暴力要把数分解。

#include<iostream>
#include<cstdio>
#include<cstring>
#include <tr1/unordered_map>
using namespace std;
using namespace std::tr1;

#define LL unsigned __int64
#define MP make_pair
const int N=30;
const LL BEI=(LL)1e16+1;
int a[N];
unordered_map<LL,LL> mp;
LL ans=0;

int n,k;
LL s;
LL f[50];
LL fac(int x){
	if(x<50) return f[x];
	return 0;
}
void predo(int x){
	LL sum=0;
	int p=0;
	int cnt=0;
	int cx=x;
	while(x){
		int ch=x%3;
		if(ch==1) sum+=(LL)a[p];
		else if(ch==2){
			if(fac(a[p])==0) return ;
			else sum+=fac(a[p]);
			++cnt;
			if(cnt>k) return ;
		}
		if(sum>s) return ;
		x/=3;
		++p;
	}
	//cout<<"q"<<' '<<cx<<' '<<sum<<' '<<cnt<<endl;
	//++mp[MP(sum,cnt)];
	//cout<<BEI*(cnt+1)+sum<<endl;
	++mp[BEI*(cnt+1)+sum];
	if(sum==s) ++ans;
}
void gao(int n1,int x){
	LL sum=0;
	int p=n1;
	int cnt=0;
	int cx=x;
	while(x){
		int ch=x%3;
		if(ch==1) sum+=(LL)a[p];
		else if(ch==2){
			if(fac(a[p])==0) return ;
			else sum+=fac(a[p]);
			++cnt;
			if(cnt>k) return ;
		}
		if(sum>s) return ;
		x/=3;
		++p;
	}
	//cout<<cx<<' '<<sum<<' '<<cnt<<endl;
	if(s==sum) ++ans;
	else{
		for(int i=0;i<=k-cnt;++i)
			ans+=mp[BEI*(i+1)+(s-sum)];
	}
}
int main(){
	scanf("%d%d%I64u",&n,&k,&s);
	memset(f,0,sizeof(f));
	f[0]=1;
	for(int i=1;i<50;++i){
		f[i]=f[i-1]*i;
		//cout<<f[i]<<endl;
		if(f[i]>s){
			f[i]=0;
			break;
		}
	}
	for(int i=0;i<n;++i)
		scanf("%d",&a[i]);
	int n1=n/2;
	int lim1=1;
	for(int i=0;i<n1;++i) lim1*=3;
	for(int i=0;i<lim1;++i){
		predo(i);
	}
	int n2=n-n1;
	int lim2=1;
	for(int i=0;i<n2;++i) lim2*=3;
	for(int i=1;i<lim2;++i){
		gao(n1,i);
	}
	printf("%I64u\n",ans);
	return 0;
}

深搜写法,比较短,比较快。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<tr1/unordered_map>
using namespace std;
using namespace std::tr1;
#define PB push_back
#define LL __int64
LL S;
LL fac[50];
int a[30];
int n,k,lim;
LL ans=0;
unordered_map<LL,int> mp;
vector<LL> res1[30];
vector<LL> res2[30];
void gao(int cur,LL sum,int e){
	if(sum>S) return ;
	if(e>k) return ;
	if(cur==lim)
		res1[e].PB(sum);
	else{
		gao(cur+1,sum,e);
		gao(cur+1,sum+a[cur],e);
		if(a[cur]<19)
			gao(cur+1,sum+fac[a[cur]],e+1);
	}
}
int main(){
	fac[0]=1;
	for(int i=1;i<19;++i){
		fac[i]=fac[i-1]*i;
	}
	scanf("%d%d%I64d",&n,&k,&S);
	for(int i=0;i<n;++i)
		scanf("%d",&a[i]);
	lim=n/2;
	gao(0,0,0);
	//for(int i=0;i<30;++i){
	//	cout<<i<<':';
	//	for(int j=0;j<res1[i].size();++j)
	//		cout<<res1[i][j]<<' ';
	//	cout<<endl;
	//}
	reverse(a,a+n);
	lim=n-lim;
	swap(res1,res2);
	gao(0,0,0);
	for(int i=k;i>=0;--i){
		for(int j=0;j<res2[k-i].size();++j){
			++mp[S-res2[k-i][j]];
		}
		for(int j=0;j<res1[i].size();++j){
			ans+=mp[res1[i][j]];
		}
	}
	printf("%I64d\n",ans);
	return 0;
}

时间: 2024-10-13 12:03:03

codeforces 525 E Anya and Cubes 中途相遇法的相关文章

Codeforces Round #297 (Div. 2) E题. Anya and Cubes (中途相遇法)

题目地址:Anya and Cubes 比赛的时候居然没想起中途相遇法...这题也是属于想起来就很简单系列. 中途相遇法也叫折半搜索.就是处理前一半,把结果储存起来,再处理后一半,然后匹配前一半存储的结果. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib

Codeforces 525E Anya and Cubes 中途相遇法

题目链接:点击打开链接 题意: 给定n个数,k个感叹号,常数S 下面给出这n个数. 目标: 任意给其中一些数变成阶乘,至多变k个. 再任意取一些数,使得这些数和恰好为S 问有多少方法. 思路: 三进制状压,中途查找. #include <stdio.h> #include <vector> #include <algorithm> #include <iostream> #include <cmath> #include <map>

CodeForces Round #297 Div.2 E (中途相遇法)

当时打比赛的时候卡在D题了,没有看E.现在看来E还是不难的. 将n个数排序后,其实不排序也是可以的,只是排序能快一半的时间. 枚举前一半能得到多少种和,放到map里面: 然后在后一半数中枚举,然后在map里面查找. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 const int maxn = 30; 6 LL a[maxn], f[maxn], S, ans; 7 int n, m,

Codeforces 31E TV Game 中途相遇法 状压dp

题目链接:点击打开链接 题意: 给定2*n长的数字. 把这个数字拆成2个长度为n的数字,且相对位置不变.使得拆后得到的2个数字的和最大. 输出一个方案. 显然是中途相遇法,先计算左半段,再计算右半段 分别状压左半段和右半段,注意左半段状压后要在末尾补上0. 代码估计哪里有小越界==,数组开大了一点才过..具体就不查了. #include<iostream> #include<stdio.h> #include<string.h> #include<string&g

【中途相遇法】【STL】BAPC2014 K Key to Knowledge

题目链接: http://codeforces.com/gym/100526 http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11674&courseid=0 题目大意: N个学生M道题(1<=N<=12,1<=M<=30),每道题只有正误两种选项(0 1),每个学生的答题情况和正确题数已知,求标准答案可能有多少种. 如果标准答案只有一种则输出标准答案,否则输出解的个数. 题目思路: [

uva 6757 Cup of Cowards(中途相遇法,貌似)

uva 6757 Cup of CowardsCup of Cowards (CoC) is a role playing game that has 5 di?erent characters (Mage, Tank, Fighter,Assassin and Marksman). A team consists of 5 players (one from each kind) and the goal is to kill amonster with L life points. The

uva1152 - 4 Values whose Sum is 0(枚举,中途相遇法)

用中途相遇法的思想来解题.分别枚举两边,和直接暴力枚举四个数组比可以降低时间复杂度.可是我不会写...看了紫书作者刘汝佳老师的代码,真是太美了!简单明了,就像看吕钦下的棋一样.我就模仿的写了一下: #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<map> #include<set>

POJ 1840 Eqs Hash + 中途相遇法

把等式分成两拨算用中途相遇法就好了. 不过要注意的是这里不能用map,会超时,要自己手写hash,我重载了[]操作符之后用起来和map差不多,很随意 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <qu

HDU 5936 Difference 【中途相遇法】(2016年中国大学生程序设计竞赛(杭州))

Difference Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 62    Accepted Submission(s): 19 Problem Description Little Ruins is playing a number game, first he chooses two positive integers y an