ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

2021-08-11 07:33:23  阅读:249  来源: 互联网

标签:std GCC 洛谷 int 2021.8 optimize pragma include 联考


目录
[2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

题目

思路

震惊,某OJ的评测机竟然比学校电脑室的电脑还慢!!!

这题是比较裸的折半搜索,关于折半搜索,引入一题:

洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

题目

题目描述

译自 CEOI2015 Day2 T1「Ice Hockey World Championship

今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

输入格式

第一行,两个正整数 \(N\) 和 \(M(1 \leq N \leq 40,1 \leq M \leq 10^{18})\),表示比赛的个数和 Bobek 那家徒四壁的财产。

第二行,\(N\) 个以空格分隔的正整数,均不超过 \(10^{16}\),代表每场比赛门票的价格。

输出格式

输出一行,表示方案的个数。由于 NNN 十分大,注意:答案\(\le 2^{40}\)。

输入输出样例

输入 #1

5 1000
100 1500 500 500 1000

输出 #1

8

说明/提示

样例解释

八种方案分别是:

  • 一场都不看,溜了溜了
  • 价格 \(100\) 的比赛
  • 第一场价格 \(500\) 的比赛
  • 第二场价格 \(500\) 的比赛
  • 价格 \(100\) 的比赛和第一场价格 \(500\) 的比赛
  • 价格 \(100\) 的比赛和第二场价格 \(500\) 的比赛
  • 两场价格 \(500\) 的比赛
  • 价格 \(1000\) 的比赛

有十组数据,每通过一组数据你可以获得 10 分。各组数据的数据范围如下表所示:

数据组号 \(1-2\) \(3-4\) \(5-7\) \(8-10\)
\(N \leq\) \(10\) \(20\) \(40\) \(40\)
\(M \leq\) \(10^6\) \(10^{18}\) \(10^6\) \(10^{18}\)

折半搜索

为了减小递归树的深度,我们把所有比赛的门票分成两部分:1到\(n/2\)​和\(n/2+1\)​到\(n\)​,各进行一次搜索,枚举门票的购买情况,一次搜索的时间就是\(O(2^{n/2}\le 2^{20})\)​.再用二分合并两部分搜索的答案,时间复杂度:\(O(2^{n/2}\cdot \log 2^{n/2})\)​,可以通过.

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

#define DEBUG 0

using std::cout;
using std::vector;
typedef long long lint ;
const int N = 50;

lint read() {
	lint re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}

int n;
lint m;
lint pri[N];
vector <lint> a , b;//分别存两部分搜索的结果

void dfs(int x , int end , lint curpri/*当前价格*/ , vector<lint> &p) {
	if(curpri > m)//剪枝,同时避免越界
		return;
	if(x == end + 1) {
		p.push_back(curpri);
		return;
	}
	dfs(x + 1 , end , curpri + pri[x] , p);//选第x张
	dfs(x + 1 , end , curpri , p);//不选
}
int main() {
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i++)
		pri[i] = read();
		
	dfs(1 , n / 2 , 0 , a);
	dfs(n / 2 + 1 , n , 0 , b);
	//合并答案
	std::sort(b.begin() , b.end());
	lint ans = 0;
	for(int i = 0 ; i < a.size() ; i++) {
		int l = 0 , r = b.size() - 1;
		while(l < r) {
			int mid = (l + r) / 2;
			if(b[mid] <= m - a[i])
				l = mid + 1;
			else
				r = mid;
		}
		if(b[l] + a[i] > m)
			--l;
		ans += l + 1;
	}
	cout << ans << '\n';
	
#if DEBUG
	for(int i = 0 ; i < a.size() ; i++)
		cout << a[i] << ' ';
	cout << '\n';
	for(int i = 0 ; i < b.size() ; i++)
		cout << b[i] << ' ';
	cout << '\n';
#endif
	return 0;
}

回到这题,也是折半搜索,但是对复杂度的要求较高,合并的时候需要哈希,具体看代码

代码

#include <iostream>
#include <cstdio>
#include <bitset>
#include <vector>
#include <cstring>

//#pragma GCC optimized(2)
#define DEBUG 0
#define reg register
#define optimize__ 1//优化开关,关闭后用链表存储路线的hash值,比较安全(冲突概率低),但是慢(GMOJ的老爷机3s跑不出本地1.8s跑出来的数据)

//火车头
//#pragma GCC optimize(3)
//#pragma GCC target("avx")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
//#pragma GCC optimize("-fgcse")
//#pragma GCC optimize("-fgcse-lm")
//#pragma GCC optimize("-fipa-sra")
//#pragma GCC optimize("-ftree-pre")
//#pragma GCC optimize("-ftree-vrp")
//#pragma GCC optimize("-fpeephole2")
//#pragma GCC optimize("-ffast-math")
//#pragma GCC optimize("-fsched-spec")
//#pragma GCC optimize("unroll-loops")
//#pragma GCC optimize("-falign-jumps")
//#pragma GCC optimize("-falign-loops")
//#pragma GCC optimize("-falign-labels")
//#pragma GCC optimize("-fdevirtualize")
//#pragma GCC optimize("-fcaller-saves")
//#pragma GCC optimize("-fcrossjumping")
//#pragma GCC optimize("-fthread-jumps")
//#pragma GCC optimize("-funroll-loops")
//#pragma GCC optimize("-fwhole-program")
//#pragma GCC optimize("-freorder-blocks")
//#pragma GCC optimize("-fschedule-insns")
//#pragma GCC optimize("inline-functions")
//#pragma GCC optimize("-ftree-tail-merge")
//#pragma GCC optimize("-fschedule-insns2")
//#pragma GCC optimize("-fstrict-aliasing")
//#pragma GCC optimize("-fstrict-overflow")
//#pragma GCC optimize("-falign-functions")
//#pragma GCC optimize("-fcse-skip-blocks")
//#pragma GCC optimize("-fcse-follow-jumps")
//#pragma GCC optimize("-fsched-interblock")
//#pragma GCC optimize("-fpartial-inlining")
//#pragma GCC optimize("no-stack-protector")
//#pragma GCC optimize("-freorder-functions")
//#pragma GCC optimize("-findirect-inlining")
//#pragma GCC optimize("-fhoist-adjacent-loads")
//#pragma GCC optimize("-frerun-cse-after-loop")
//#pragma GCC optimize("inline-small-functions")
//#pragma GCC optimize("-finline-small-functions")
//#pragma GCC optimize("-ftree-switch-conversion")
//#pragma GCC optimize("-foptimize-sibling-calls")
//#pragma GCC optimize("-fexpensive-optimizations")
//#pragma GCC optimize("-funsafe-loop-optimizations")
//#pragma GCC optimize("inline-functions-called-once")
//#pragma GCC optimize("-fdelete-null-pointer-checks")
//#pragma GCC optimize(2)


using std::memset;
using std::cin;
using std::cout;
using std::vector;
typedef long long lint;
typedef unsigned long long ulint;
typedef unsigned int unint;
const int N = 20;

const int mod = 1e8 + 7;
int head[mod + 10];
struct hashing {
	int id , nxt;
	ulint key;
} chain[(int)3991680 + 10];

int n , m;
int map[N][N];

struct ROUTE {
	struct bitset {
		unint a;
		inline void set() {
			a = -1;
		}
		inline void reset() {
			a = 0;
		}
		inline void change(int pos , int x) {
			if(!x)
				a &= ((-1) ^ (1u << pos));
			else
				a |= (1u << pos);
		}
		bool operator [] (const int &pos) {
			return (a >> pos) & 1;
		}
	};

	lint len;
	bitset vis;
	ROUTE() {
		len = 0;
		vis.reset();
	}
	void clear() {
		len = 0;
		vis.reset();
	}
};

typedef vector<ROUTE> vecr;

void dfs(int pre , int end , int curnum , int maxnum , ROUTE r , vecr &vec) {
	if(r.len > m)
		return;
	if(curnum == maxnum - 1) {
		r.len += map[pre][end];
		vec.push_back(r);
		return;
	}

	for(int i = 1 ; i <= n ; i++) {
		if(r.vis[i] || i == end)
			continue;
		r.vis.change(i , true);
		r.len += map[pre][i];
		dfs(i , end , curnum + 1 , maxnum , r , vec);
		r.vis.change(i , false);
		r.len -= map[pre][i];
	}
}

/*
inline int MinHash(reg ROUTE a) {
	lint res = a.len % mod;
	for(int i = 1 ; i <= n ; i++)//旧的STLbitset,比较慢
		res = (res * 3 + a.vis[i]) % mod;
	return res;
}
inline ulint ULHash(reg ROUTE a) {
	ulint res = a.len;
	for(int i = 1 ; i <= n ; i++)
		res = (res * 3 + a.vis[i]);
	return res;
}

/*/
inline int MinHash(reg ROUTE a) {
	static lint pow = 0 , l;
	if(pow == 0) {
		l = (1 << (n + 1)) - 2;
		pow = 1;
		for(int i = 0 ; i <= n ; i++)
			pow = (pow * 3ll) % mod;
	}

	return ((ulint)a.len * pow + (a.vis.a & l)) % mod;
}
inline ulint ULHash(reg ROUTE a) {
	static ulint pow = 0 , l;
	if(pow == 0) {
		l = (1 << (n + 1)) - 2;
		pow = 1;
		for(int i = 0 ; i <= n ; i++)
			pow *= 3ull;
	}
	return (ulint)a.len * pow + (a.vis.a & l);
}//*/
int main() {
	freopen("way.in" , "r" , stdin);
	freopen("way.out" , "w" , stdout);

	cin >> n >> m;
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++)
			cin >> map[i][j];

	vecr l , r , empt;
	empt.clear();

	reg int ans = 0;
	
	memset(head , 0 , sizeof(head));
	for(int i = 2 ; i <= n ; i++) {

		l.clear() , r.clear();
		reg ROUTE tmp;

		tmp.vis.change(1 , true);

		int mid = (n + 2) / 2;
		dfs(1 , i , 1 , mid , tmp , l);
		tmp.clear();

		tmp.vis.change(i , true);
		dfs(i , 1 , 1 , n + 2 - mid , tmp , r);

#if optimize__
#else
		memset(head , 0 , sizeof(head));
		memset(chain , 0 , sizeof(chain));
#endif

		int cnt = 0;
		int siz = 0;

		siz = r.size();
		for(reg int j = 0 ; j < siz ; ++j) {
			ulint key2 = MinHash(r[j]);
#if optimize__
			++head[key2];//可能冲突,但是快
#else
			ulint key1 = ULHash(r[j]);//极小概率冲突,但是慢
			++cnt;
			chain[cnt].key = key1 , chain[cnt].id = j , chain[cnt].nxt = head[key2] , head[key2] = cnt;
#endif
		}
		



		siz = l.size();
		for(reg int j = 0 ; j < siz ; ++j) {
			tmp.len = m - l[j].len;//tmp:与l[j]匹配的路线(不重不漏经过所有点,长度之和为m)
			tmp.vis.set();
			tmp.vis.a = tmp.vis.a ^ l[j].vis.a;

#if optimize__
			ans += head[MinHash(tmp)];
#else
			reg ulint key = ULHash(tmp);
			for(reg int k = head[MinHash(tmp)] ; k ; k = chain[k].nxt) {
				if(key == chain[k].key) {
					++ans;
				}
			}
#endif
		}
		
		
#if optimize__
		siz = r.size();//由于head比较大,这样会比memset块
		for(reg int j = 0 ; j < siz ; ++j) {
			ulint key2 = MinHash(r[j]);
			head[key2] = 0;
		}
#endif
	}
	cout << ans;
	return 0;
}

附60分做法-状压DP

\(l\)比较下,考虑\(f_{k,i,j}\)表示状态为\(k\),路径最后一个点为\(i\),路径长度为\(j\)的方案数,用dfs转移即可

另外,我们可以对\(i,j,k\)​做Hash或者map,就可以跑\(k>30\)​​的情况,用Hash极致优化一下,可以额外得到10pts的高分(最后是状态太多了存不下,时间也爆了):

//数组+map集成板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>

typedef long long lint;
using std::pair;
using std::cout;
using std::make_pair;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}


const int N = 20;
const int Status = 1 << 14;
const int L = 50;

int n , l;

int map[N][N];

namespace LargeL {
	typedef pair<pair<int , int> , int> tripair;
	tripair make(int a , int b , int c) {
		tripair tmp;
		tmp = make_pair(make_pair(a , b) , c);
		return tmp;
	}
	std::map <tripair , int> f;

	lint dfs(int k , int las , int len) {
		if(len < 0)
			return 0;

		tripair P = make(k , las , len);
		if(f.find(P) != f.end())
			return f[P];

		int sum = 0;
		for(int i = 0 ; i < n ; i++)
			if(i != las && (k >> i & 1) == 1)
				sum += dfs(k ^ (1 << las) , i , len - map[i][las]);
		return f[P] = sum;
	}
	void work() {
		f[make(1 , 0 , 0)] = 1;
		lint ans = 0;
		int final = (1 << n) - 1;
		for(int i = 1 ; i < n ; i++)
			ans += dfs(final , i , l - map[i][0]);
		cout << ans;
	}
}
namespace SmallL {
	lint f[Status][N][L];
	lint dfs(int k , int las , int len) {

		if(len < 0)
			return 0;
		if(f[k][las][len] != -1)
			return f[k][las][len];

		f[k][las][len] = 0;
		for(int i = 0 ; i < n ; i++)
			if(i != las && (k >> i & 1) == 1)
				f[k][las][len] += dfs(k ^ (1 << las) , i , len - map[i][las]);

		return f[k][las][len];
	}
	void work() {
		memset(f , -1 , sizeof(f));
		f[1][0][0] = 1;
		lint ans = 0;
		int final = (1 << n) - 1;
		for(int i = 1 ; i < n ; i++)
			ans += dfs(final , i , l - map[i][0]);
		cout << ans;
	}
}

int main() {
//	std::freopen("way.in" , "r" , stdin);
//	std::freopen("way.out" , "w" , stdout);
	
	n = read() , l = read();
	for(int i = 0 ; i < n ; i++)
		for(int j = 0 ; j < n ; j++)
			map[i][j] = read();

	if(l <= 30)
		SmallL::work();
	else
		LargeL::work();
	return 0;
}
//Hash板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>


typedef long long lint;
using std::cout;
using std::make_pair;
using std::pair;
const int N = 20;
const int Status = 1 << 14;
const int L = 50;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
class Hashing {
	private :
		typedef unsigned long long ulint;
		static const ulint mod = 10000009;
		struct node {
			ulint key;
			int dat;
			int nxt;
		}chain[N * L * Status];
		inline ulint hash1(ulint a , ulint b , ulint c) {
			return a * (N + 1) * (L + 1) + b * (L + 1) + c;
		}
		inline ulint hash2(ulint a , ulint b , ulint c) {
			return (a * (N + 1) % mod * (L + 1) % mod + b * (L + 1) % mod + c) % mod;
		}
		int head[mod + 10];
		int cnt;
	public :
		int find(ulint a , ulint b , ulint c) {
			int key = hash1(a , b , c);
			for(int i = head[hash2(a , b , c)] ; i ; i = chain[i].nxt) {
				if(key == chain[i].key)
					return chain[i].dat;
			}
			return -1;
		}
		inline void insert(ulint a , ulint b , ulint c , int dat) {
			if(find(a , b , c) != -1)
				return;
			ulint k1 = hash1(a , b , c) , k2 = hash2(a , b , c);
			++cnt;
			chain[cnt].key = k1 , chain[cnt].dat = dat , chain[cnt].nxt = head[k2] , head[k2] = cnt;
			
		}
};


int n , l;

int map[N][N];


typedef pair<pair<int , int> , int> tripair;
tripair make(int a , int b , int c) {
	tripair tmp;
	tmp = make_pair(make_pair(a , b) , c);
	return tmp;
}

Hashing f;

lint dfs(int k , int las , int len) {
	if(len < 0)
		return 0;
	
	int tmp = f.find(k , las , len);
	if(tmp != -1)
		return tmp;
	
	int sum = 0;
	for(int i = 0 ; i < n ; i++)
		if(i != las && (k >> i & 1) == 1)
			sum += dfs(k ^ (1 << las) , i , len - map[i][las]);
	f.insert(k , las , len , sum);
	return sum;
}
int main() {
	std::freopen("data//way8.in" , "r" , stdin);
	std::freopen("way.out" , "w" , stdout);
	n = read() , l = read();
	for(int i = 0 ; i < n ; i++)
		for(int j = 0 ; j < n ; j++)
			map[i][j] = read();
	
	
	f.insert(1 , 0 , 0 , 1);
	
	lint ans = 0;
	int final = (1 << n) - 1;
	for(int i = 1 ; i < n ; i++)
		ans += dfs(final , i , l - map[i][0]);
	cout << ans;
	return 0;
}

标签:std,GCC,洛谷,int,2021.8,optimize,pragma,include,联考
来源: https://www.cnblogs.com/dream1024/p/15126603.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有