2021CCPC 哈尔滨(B D E I J)

2021CCPC 哈尔滨(B D E I J)

Dashboard - The 2021 China Collegiate Programming Contest (Harbin) - Codeforces

B. Magical Subsequence(贪心 + dp)

对于本题 , 可以注意到可以组成的数的值域非常小 , 只有 [2 , 200] , 我们不妨从可以组成的数下手 , 枚举每一个可以组成的数 , 确定组成当前数的最大贡献。如何确定这个最大贡献呢 , 考虑一个经典 dp

d p [ i ] [ j ] 表示组成数字 i ,数列前 j 位的最大贡献。 dp[i][j] 表示组成数字 i , 数列前 j 位的最大贡献。 dp[i][j]表示组成数字i,数列前j位的最大贡献。

考虑状态转移 , 不难想出 , 第 j 位有两种选择

1: 第 j 位不参与组成 i

d p [ i ] [ j ] = m a x ( d p [ i ] [ j ]   ,   d p [ i − 1 ] [ j ] ) dp[i][j] = max(dp[i][j]~,~dp[i - 1][j]) dp[i][j]=max(dp[i][j] , dp[i1][j])

2. 第 j 位参与组成 i

d p [ i ] [ j ] = m a x ( d p [ i ] [ j ]   ,   d p [ i ] [ p o s [ i − a [ j ] ] − 1 ] + 2 ) dp[i][j] = max(dp[i][j]~,~dp[i][pos[i-a[j]]-1]+2) dp[i][j]=max(dp[i][j] , dp[i][pos[ia[j]]1]+2)

这里的 pos[i - a[j]] 指的是当前位可以从前面所有等于 i - a[j]的位置转移过来 , 但是显然这么写时间复杂度是 n^2 的 , 不能接受。考虑贪心 , 我们只从前面等于 i - a[j] 的最后一个位置转移 , 因为前面位置的贡献是一定不可能有最后一个位置优的 , 这样就能做到线性复杂度。

时间复杂度 O ( 200 n ) 时间复杂度O(200n) 时间复杂度O(200n)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int n , a[N] , maxx;

int pos[310] , dp[210][N];

/*
和为 i 前 j 个数的最大贡献
*/

signed main(){

	IOS
	cin >> n;
	for(int i = 1 ; i <= n ; i ++ ) cin >> a[i];
	for(int i = 2 ; i <= 200 ; i ++) {
		for(int j = 1 ; j <= 100 ; j ++) pos[j] = 0;
		for(int j = 1 ; j <= n ; j ++) {
			if(a[j] >= i || !pos[i - a[j]]) dp[i][j] = dp[i][j - 1];
			else dp[i][j] = max(dp[i][j - 1] , dp[i][pos[i - a[j]] - 1] + 2);
			pos[a[j]] = j;
		}
	}
	for(int i = 2 ; i <= 200 ; i ++) maxx = max(maxx , dp[i][n]);
	
	cout << maxx << "\n";

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

D. Math master(枚举子集 , meet in the middle)

思路:可以注意到每一个数字最多只有 18 位 , 考虑枚举子集 。先枚举分子的子集 , 把枚举出来的所有需要删的数和剩余的分子存起来 , 然后对分母枚举子集 , 可以得到要删去的数和剩余的分母 , 用分母求出分子 , 然后查询即可。

时间复杂度 O ( n 2 n l o g ( 2 n ) ) 时间复杂度O(n2^nlog(2^n)) 时间复杂度O(n2nlog(2n))

由于比标程复杂度多个log ,复杂度比较极限,所以需要实现的精细一点。

用分母求分子的时候会爆 ll , 用 __int128 转化即可

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 30 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int t , a[N] , b[N] , n , m , st , ed;
string s1 , s2;

signed main(){

	IOS
	cin >> t;
	
	while(t --){
		cin >> s1 >> s2;
		n = s1.size();
		m = s2.size();
		st = ed = 0;
		for(int i = 0 ; i < n ; i ++) a[i] = s1[i] - '0' , st = st * 10 + a[i];
		for(int i = 0 ; i < m ; i ++) b[i] = s2[i] - '0' , ed = ed * 10 + b[i];
		
		set<pair<string , int>>st1;
		
		for(int i = 1 ; i <= (1ll << n) ; i++){
			int now = 0;
			string del;
			for(int j = 0 ; j < n ; j ++){
				if((i >> j) & 1){
					del += a[j] + '0';
				}else{
					now = now * 10 + a[j];
				}	
			}
			sort(del.begin() , del.end());
			st1.insert({del , now});
		}
		
		int ans = 9e18 , els = 0;
		
		for(int i = 1 ; i <= (1ll << m) ; i++){
			int now = 0;
			string del;
			for(int j = 0 ; j < m ; j ++){
				if((i >> j) & 1){
					del += b[j] + '0';
				}else{
					now = now * 10 + b[j];
				}	
			}
			sort(del.begin() , del.end());
			if(!now) continue;
			if((__int128) st * now % (__int128) ed) continue;
			int pre = (__int128) st * now / ed;
			if(st1.find({del , pre}) != st1.end()){
				if(pre < ans){
					ans = pre;
					els = now;
				}
			}
		}	
		cout << ans << " " << els << "\n";
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

E. Power and Modulo(思维)

思路:不难发现一个小性质 , 那就是

A i = ( A i − 1 ∗ 2 ) % M ( A 1 = 0 ∣ ∣ A 1 = 1 ) A_i = (A_{i-1}*2)\%M(A_1=0||A_1 = 1) Ai=(Ai12)%MA1=0∣∣A1=1

这个 M 应该是唯一确定的. 那我们只需要判断 M 是否存在且唯一即可 , 注意特判 M = 1 , 即 A 数组全为 0 的情况。

时间复杂度 O ( n l o g n ) 时间复杂度O(nlogn) 时间复杂度O(nlogn)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int t , n;
int a[N] , res;

bool judge(){
	
	int cnt = 0;
	for(int i = i = 1 ; i <= n ; i ++) if(!a[i]) cnt += 1;
	if(cnt == n) {
		res = 1;
		return 1;
	}
	
	set<int>ans;
	if(a[1] < 0 || a[1] > 1) return 0;
	for(int i = 2 ; i <= n ; i ++){
		if(a[i] >= a[i - 1]){
			if(a[i - 1] * 2 != a[i]) return 0;
		}else{
			res = a[i - 1] * 2 - a[i];
			ans.insert(a[i - 1] * 2 - a[i]);
		}
	}
	if(ans.size() != 1) return 0;
	else return 1;
	
}

signed main(){

	IOS
	cin >> t;
	
	while(t --){
		
		cin >> n;
		for(int i = 1 ; i <= n ; i ++) cin >> a[i];
		if(judge()){
			cout << res << "\n";
		}else{
			cout << "-1\n";
		}
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

I. Power and Zero(贪心)

思路:不难想到需要计算二进制每一位的数量 ,所以问题转化成 需要把二进制的数量数组变成 低位大 , 高位小的阶梯型数组 , 而且需要最低位的最大值最小 , 这个值即是我们需要的答案 。不难发现 , 对于数组的转换 , 一个高位可以转化成两个相邻的低位 , 即 cnt[i] → 2 * cnt[i - 1] , 反过来却不行 , 即只能由高位补低位 , 我们考虑贪心的去补位 , 对于每一位 , 只要比低位大就向低位补位 , 补到阶梯型为止。

时间复杂度不太会算,但是跑的飞快 时间复杂度不太会算 , 但是跑的飞快 时间复杂度不太会算,但是跑的飞快

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int t , n;
int a[N] , bit[32];

signed main(){

	IOS
	cin >> t;
	
	while(t --) {
		cin >> n;
		
		for(int i = 0 ; i <= 30 ; i ++) bit[i] = 0;
		
		for(int i = 1 ; i <= n ; i ++) {
			cin >> a[i];
			for(int j = 0 ; j <= 30 ; j ++) {
				if(a[i] >> j & 1) bit[j] += 1;
			}
		}
		
		bool tag = true;
		
		while(tag) {
			
			tag = false;
			
			for(int i = 30 ; i >= 1 ; i --) {
				if(bit[i] > bit[i - 1]) {
					int need = (bit[i] - bit[i - 1] - 1) / 3 + 1;
					bit[i] -= need;
					bit[i - 1] += need * 2;
					tag = true;
				}
			}
		}
		

		cout << bit[0] << "\n";
		
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

J. Local Minimum(签到)

计算即使行最值又是列最值的元素个数即可。

时间复杂度 O ( n m ) 时间复杂度O(nm) 时间复杂度O(nm)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 1e3 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int a[N][N] , n , m , res;
bool vis[N][N];

signed main(){

	IOS
	cin >> n >> m;
	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 1 ; j <= m ; j ++) {
			cin >> a[i][j];
		}
	}
	for(int i = 1 ; i <= n ; i ++) {
		int minn = 9e18;
		for(int j = 1 ; j <= m ; j ++) minn = min(minn , a[i][j]);
		for(int j = 1 ; j <= m ; j ++) if(a[i][j] == minn) vis[i][j] = 1;
	}
	
	for(int j = 1 ; j <= m ; j ++) {
		int minn = 9e18;
		for(int i = 1 ; i <= n ; i ++) minn = min(minn , a[i][j]);
		for(int i = 1 ; i <= n ; i ++) if(a[i][j] == minn) {
			if(vis[i][j]) res += 1;
		}
	}
	
	cout << res << "\n";

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

算法讨论小群 : 545214567(QQ)

你可能感兴趣的:(算法)