第五届河南省CCPC河南省省赛题解+复盘

第五届河南省CCPC河南省省赛题解+复盘

今年省赛相当有意思的一点,是20级第一次线下省赛,对于部分队也可能是最后一次,看队名就能看出来很多 考研就业的选手,一群老年人在这PK,氛围挺不错。

A - 小水獭游河南 — 签到

这个题关键点就是 知道a串最多有26个字母,超过26个字母一定会重复
同时注意,一旦发生重复后边也一定会重复,记得break
还要要特判一下s长度为1的情况

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector> 
using namespace std;

// 判断回文 
bool check(string s, int st)
{
	for(int i = st, j = s.size() - 1; i < j; i ++, j --)
		if(s[i] != s[j]) return false;
	return true;
}

void solve()
{
	string s; cin >> s;
	vector<int> cnt(26, 0);
	
	if(s.size() == 1) {
		cout << "NaN\n";
		return ;
	}
	
	// 最多26个字母,st代表后边回文字符串的开始位置 
	for(int st = 1; st <= 26; st ++)
	{
		// 之前的计数 
		int t = s[st - 1] - 'a';
		// 判断是否重复, 重复直接break 
		if(cnt[t])  {
			break;
		}
		cnt[t] ++;
		
		// 检查是否回文 
		if(check(s, st)) 
		{
			cout << "HE\n";
			return;
		}
	}
	cout << "NaN\n";
}

int main()
{
	int T; cin >> T;
	while(T --) solve();
}

B - Art for Rest – 找性质+前缀/后缀处理

原题意是 把一个长度为n的数组,分为每段长度为k的区间(最后一个区间可能不足k),每个区间单独排序,区间排序后拼接形成整个数组。求满足拼接形成的数组是非严格递增的 k 的 数量

题目可转化为 求符合 每一段长度为k 的区间的最大值 小于等于 后缀数组所有数的最小值的 k的取值数量
又可以转化为 求符合 每个区间最后一个位点 前缀最大值 小于等于 后缀最小值的 k的取值数量
直接暴力即可,调和级数复杂度nlogn

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector> 
using namespace std;

int main()
{
	int n; cin >> n;
	vector<int> a(n + 1), minv(n + 2), st(n + 2, 0);
	
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	
	// 求后缀最小值 
	minv[n + 1] = 2e9;
	for(int i = n; i >= 1; i --)
		minv[i] = min(minv[i + 1], a[i]);
	
	// st[i] = 1 表示前i个数里的最大值小于 后缀所有数的最小值 
	int maxv = 0;
	for(int i = 1; i <= n; i ++)
	{
		maxv = max(maxv, a[i]);
		if(maxv <= minv[i + 1] || i == n)
			st[i] = 1; 
	}
	
	
	int res = 0;
	// 对于每一个长度为k的区间,看最后一个位置 st[i] 全部等于1 即满足条件,答案 + 1 
	for(int k = 1; k <= n; k ++)
	{
		int f = 1; 
		for(int i = k; i <= n; i += k)
		{
			if(st[i] == 0)
			{
				f = 0;
				break;
			}
		}
		res += f;
	}
	printf("%d\n", res);
}

C - Toxel 与随机数生成器 — 思维题

因为|s| = 1e6 每段长度1e3到1e4,直接暴力如果重复出现100次以上就判为No

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector> 
using namespace std;

int main()
{
	string s; cin >> s;
	// 记录重复数量 
	int cnt = 0;
	string tar = s.substr(0, 1000);
	for(int i = 0; i + 1000 < s.size(); i ++)
	{
		if(tar == s.substr(i, 1000)){
			cnt ++;
			i += 1000;
		}
	}
	if(cnt < 100) cout << "Yes\n";
	else cout << "No\n";
}

E - 矩阵游戏 — dp

官方题解说的很清楚,不过需要注意的是三维转二维的过程,很容易出现错误,用一个新的数组记录上一个状态
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
 
void solve()
{
	int n, m, x; cin >> n >> m >> x;
	vector<vector<int>> f(m + 1, vector<int>(x + 1)), g(f);
	vector<string> s(n + 1);
	for (int i = 1; i <= n; i++) cin >> s[i], s[i] = " " + s[i];
 
	int res = 0;
	// g 对应的其实就是 f[i-1][j][k] f对应 f[i][j][k]
	for (int i = 1; i <= n; i++) 
	{
		for (int j = 1; j <= m; j++) 
		{
			for (int k = 0; k <= x; k++) 
			{
				if (s[i][j] == '0') f[j][k] = max(f[j - 1][k], g[j][k]);
				else if (s[i][j] == '1') f[j][k] = max(f[j - 1][k], g[j][k]) + 1;
				else 
				{
					if (k >= 1) f[j][k] = max(f[j - 1][k - 1] + 1, g[j][k - 1] + 1);
					else f[j][k] = max(f[j - 1][k], g[j][k]);
				}
				if (i == n && j == m) res = max(res, f[j][k]);
			}
		}
		g = f;
	}
	
	cout << res << "\n";	
} 

int main() 
{
	int T; cin >> T;
	while(T --) solve();
}

F - Art for Last — 贪心 + 区间求最小值(下边三种写法均可)

题意是求 从n个数中 选k个数 求k个数中(任意2个数之差的最小值)乘 (任意2数的之差最大值) 的 最小值
我们要想求最小值,就是尽量让2个数之差的最小值尽可能的小,让任意2数的之差最大值也尽可能的小
直接贪心排序一下,最大值就是长度为k的区间 最后一个-第一个。
然后就是找区间中两个数差值的最小值,差值的最小值一定在排序后相邻的元素中产生
因此找相邻元素差值最小值即可,把相邻元素差值单独取出来,用ST表或线段树或滑动窗口维护都可以,求一下区间最小值即可

所以sort后遍历一下求每个长度k区间的最大最小值,取乘积最小即可

写法一 ST表写法
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
#define int long long
const int N = 500010;
int Fmin[N][20];
int lg2[N];
int a[N];
int n, k;
void init_log()
{
    lg2[0] = -1;
    for(int i = 1; i < N; i ++) lg2[i] = lg2[i >> 1] + 1;
}
void init()
{
	for(int i = 1; i < n; i ++) Fmin[i][0] = a[i + 1] - a[i];
	int k = lg2[n];
	for(int j = 1; j <= k; j ++)
		for(int i = 1; i <= n - (1 << j) + 1; i ++)
		{
			Fmin[i][j] = min(Fmin[i][j - 1], Fmin[i + (1 << j - 1)][j - 1]);
		}
			
}
int RMQ(int l,int r)
{
	int k = lg2[r - l + 1];
	int minv = min(Fmin[l][k], Fmin[r - (1 << k) + 1][k]);
	return minv;
}

signed main() 
{
	init_log();
	
	scanf("%lld %lld",&n, &k);
	for(int i = 1; i <= n; i ++) scanf("%lld", a + i);
	sort(a + 1, a + n + 1);
	
	init();
	int res = 2e18;
	for(int i = k; i <= n; i ++)
	{
		int t = RMQ(i - k + 1, i - 1) * (a[i] - a[i - k + 1]);
		res = min(t, res); 
	}
	cout << res << '\n';
}

写法二 滑动窗口
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;

signed main()
{
	int n, k; cin >> n >> k;
	vector<int> a(n + 1), d(n + 1), q(n + 1);
	for(int i = 1; i <= n; i ++) cin >> a[i];
	sort(a.begin() + 1, a.end());
	
	for(int i = 1; i < n; i ++)
		d[i] = a[i + 1] - a[i];
	
	int hh = 0, tt = -1;
	int res = 2e18;
	for(int i = 1; i <= n; i ++)
	{
		if(hh <= tt && q[hh] < i - k + 1) hh ++;
		if(i >= k)
		{
			int t = d[q[hh]] * (a[i] - a[i - k + 1]);
			res = min(res, t);
		}
		while(hh <= tt && d[i] <= d[q[tt]]) tt --;
		q[++ tt] = i;
	}
	cout << res << '\n';
}
写法三 multiset写法
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#define int long long
using namespace std;

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n, k; cin >> n >> k;
	vector<int> a(n + 1);
	for(int i = 1; i <= n; i ++) cin >> a[i];
	sort(a.begin() + 1, a.end());
	
	multiset<int> S;
	for(int i = 1; i < k; i ++)
		S.insert(a[i + 1] - a[i]);
		
	int res = *S.begin() * (a[k] - a[1]);
	for(int i = k; i <= n; i ++)
	{
		int t = *S.begin() * (a[i] - a[i - k + 1]);
		res = min(res, t);
		S.erase(S.find(a[i - k + 2] - a[i - k + 1]));
		S.insert(a[i + 1] - a[i]);
	}
	cout << res << '\n';
}

G - Toxel 与字符画 — 大模拟

大模拟,容易出错的地方是判断结果是否超过1e18输出INF
这里不需要用什么快速幂,特判一下如果x = 1结果为1
否则只要x是比2大的数,2的60次方超过1e18,因此遍历不会超过60次
直接暴力判断即可,需要注意暴力乘的时候可能爆longlong,直接用__int128就好了

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
#define int long long
string big[11][10], sma[11][10];
string dengyu[10], inf[40];
char g[15][2010];
int col = 0;
void init()
{
    big[0][0]="........",sma[0][0]="......";
    big[0][1]="........",sma[0][1]=".00000";
    big[0][2]=".0000000",sma[0][2]=".0...0";
    big[0][3]=".0.....0",sma[0][3]=".0...0";
    big[0][4]=".0.....0",sma[0][4]=".0...0";
    big[0][5]=".0.....0",sma[0][5]=".00000";
    big[0][6]=".0.....0",sma[0][6]="......";
    big[0][7]=".0.....0",sma[0][7]="......";
    big[0][8]=".0000000",sma[0][8]="......";
    big[0][9]="........",sma[0][9]="......";

    big[1][0]="........",sma[1][0]="......";
    big[1][1]="........",sma[1][1]=".....1";
    big[1][2]=".......1",sma[1][2]=".....1";
    big[1][3]=".......1",sma[1][3]=".....1";
    big[1][4]=".......1",sma[1][4]=".....1";
    big[1][5]=".......1",sma[1][5]=".....1";
    big[1][6]=".......1",sma[1][6]="......";
    big[1][7]=".......1",sma[1][7]="......";
    big[1][8]=".......1",sma[1][8]="......";
    big[1][9]="........",sma[1][9]="......";
    
    big[2][0]="........",sma[2][0]="......";
    big[2][1]="........",sma[2][1]=".22222";
    big[2][2]=".2222222",sma[2][2]=".....2";
    big[2][3]=".......2",sma[2][3]=".22222";
    big[2][4]=".......2",sma[2][4]=".2....";
    big[2][5]=".2222222",sma[2][5]=".22222";
    big[2][6]=".2......",sma[2][6]="......";
    big[2][7]=".2......",sma[2][7]="......";
    big[2][8]=".2222222",sma[2][8]="......";
    big[2][9]="........",sma[2][9]="......";
    
    big[3][0]="........",sma[3][0]="......";
    big[3][1]="........",sma[3][1]=".33333";
    big[3][2]=".3333333",sma[3][2]=".....3";
    big[3][3]=".......3",sma[3][3]=".33333";
    big[3][4]=".......3",sma[3][4]=".....3";
    big[3][5]=".3333333",sma[3][5]=".33333";
    big[3][6]=".......3",sma[3][6]="......";
    big[3][7]=".......3",sma[3][7]="......";
    big[3][8]=".3333333",sma[3][8]="......";
    big[3][9]="........",sma[3][9]="......";

    big[4][0]="........",sma[4][0]="......";
    big[4][1]="........",sma[4][1]=".4...4";
    big[4][2]=".4.....4",sma[4][2]=".4...4";
    big[4][3]=".4.....4",sma[4][3]=".44444";
    big[4][4]=".4.....4",sma[4][4]=".....4";
    big[4][5]=".4444444",sma[4][5]=".....4";
    big[4][6]=".......4",sma[4][6]="......";
    big[4][7]=".......4",sma[4][7]="......";
    big[4][8]=".......4",sma[4][8]="......";
    big[4][9]="........",sma[4][9]="......";
    
    big[5][0]="........",sma[5][0]="......";
    big[5][1]="........",sma[5][1]=".55555";
    big[5][2]=".5555555",sma[5][2]=".5....";
    big[5][3]=".5......",sma[5][3]=".55555";
    big[5][4]=".5......",sma[5][4]=".....5";
    big[5][5]=".5555555",sma[5][5]=".55555";
    big[5][6]=".......5",sma[5][6]="......";
    big[5][7]=".......5",sma[5][7]="......";
    big[5][8]=".5555555",sma[5][8]="......";
    big[5][9]="........",sma[5][9]="......";
    
    big[6][0]="........",sma[6][0]="......";
    big[6][1]="........",sma[6][1]=".66666";
    big[6][2]=".6666666",sma[6][2]=".6....";
    big[6][3]=".6......",sma[6][3]=".66666";
    big[6][4]=".6......",sma[6][4]=".6...6";
    big[6][5]=".6666666",sma[6][5]=".66666";
    big[6][6]=".6.....6",sma[6][6]="......";
    big[6][7]=".6.....6",sma[6][7]="......";
    big[6][8]=".6666666",sma[6][8]="......";
    big[6][9]="........",sma[6][9]="......";
    
    big[7][0]="........",sma[7][0]="......";
    big[7][1]="........",sma[7][1]=".77777";
    big[7][2]=".7777777",sma[7][2]=".....7";
    big[7][3]=".......7",sma[7][3]=".....7";
    big[7][4]=".......7",sma[7][4]=".....7";
    big[7][5]=".......7",sma[7][5]=".....7";
    big[7][6]=".......7",sma[7][6]="......";
    big[7][7]=".......7",sma[7][7]="......";
    big[7][8]=".......7",sma[7][8]="......";
    big[7][9]="........",sma[7][9]="......";


    big[8][0]="........",sma[8][0]="......";
    big[8][1]="........",sma[8][1]=".88888";
    big[8][2]=".8888888",sma[8][2]=".8...8";
    big[8][3]=".8.....8",sma[8][3]=".88888";
    big[8][4]=".8.....8",sma[8][4]=".8...8";
    big[8][5]=".8888888",sma[8][5]=".88888";
    big[8][6]=".8.....8",sma[8][6]="......";
    big[8][7]=".8.....8",sma[8][7]="......";
    big[8][8]=".8888888",sma[8][8]="......";
    big[8][9]="........",sma[8][9]="......";

    big[9][0]="........",sma[9][0]="......";
    big[9][1]="........",sma[9][1]=".99999";
    big[9][2]=".9999999",sma[9][2]=".9...9";
    big[9][3]=".9.....9",sma[9][3]=".99999";
    big[9][4]=".9.....9",sma[9][4]=".....9";
    big[9][5]=".9999999",sma[9][5]=".99999";
    big[9][6]=".......9",sma[9][6]="......";
    big[9][7]=".......9",sma[9][7]="......";
    big[9][8]=".9999999",sma[9][8]="......";
    big[9][9]="........",sma[9][9]="......";

    dengyu[0]="........";
    dengyu[1]="........";
    dengyu[2]="........";
    dengyu[3]="........";
    dengyu[4]=".=======";
    dengyu[5]="........";
    dengyu[6]=".=======";
    dengyu[7]="........";
    dengyu[8]="........";
    dengyu[9]="........";

    inf[0]="........................";
    inf[1]="........................";
    inf[2]=".IIIIIII.N.....N.FFFFFFF";
    inf[3]="....I....NN....N.F......";
    inf[4]="....I....N.N...N.F......";
    inf[5]="....I....N..N..N.FFFFFFF";
    inf[6]="....I....N...N.N.F......";
    inf[7]="....I....N....NN.F......";
    inf[8]=".IIIIIII.N.....N.F......";
    inf[9]="........................";
    
}

// 判断是否超过1e18 
int check(int x, int y)
{
	if(x == 1) return 1;
	
	__int128 res = 1;
	for(int i = 1; i <= y; i ++)
	{
		res *= x;
		if(res > 1e18) return 0;
	}
	return res;
}

// 把数字提取到vector数组中 
void tiqu(int x, vector<int>& vec)
{
	while(x)
	{
		vec.push_back(x % 10);
		x /= 10;
	}
	reverse(vec.begin(),vec.end());
}

// 添加大数 
void add_big(vector<int>& vec)
{
	for(auto x : vec)
	{
		for(int i = 0; i < 10; i ++)
			for(int j = 0; j < 8;j ++)
				g[i][col + j] = big[x][i][j]; 
		col += 8;
	}
}

// 添加小数 
void add_small(vector<int>& vec)
{
	for(auto x : vec)
	{
		for(int i = 0; i < 10; i ++)
			for(int j = 0; j < 6;j ++)
				g[i][col + j] = sma[x][i][j]; 
		col += 6;
	}
}

// 添加等于号 
void add_dengyu()
{
	for(int i = 0; i < 10; i ++)
		for(int j = 0; j < 8;j ++)
		g[i][col + j] = dengyu[i][j]; 
	col += 8;
}

// 添加INF 
void add_inf()
{
	for(int i = 0; i < 10; i ++)
		for(int j = 0; j < 24;j ++)
		g[i][col + j] = inf[i][j]; 
	col += 24;	
}

// 最后一列"." 
void add_col()
{
	for(int i = 0; i < 10; i ++)
		g[i][col] = '.'; 
	col += 1;	
}

// 打印结果 
void print()
{
	for(int i = 0; i < 10; i ++)
	{
		for(int j = 0; j < col; j ++)
			cout << g[i][j];
		cout << endl;
	}
}

void solve()
{
	col = 0;
	int x, y;
	scanf("%lld^{%lld}", &x, &y);
	
	vector<int> vecx;
	tiqu(x, vecx);
	add_big(vecx);
	
	vector<int> vecy;
	tiqu(y, vecy);
	add_small(vecy);
	
	add_dengyu();
	
	int res = check(x, y);
	if(res == 0){
		add_inf();		
	}
	else{
		vector<int> vecres;
		tiqu(res, vecres);
		add_big(vecres);
	}
	add_col();
	print();
}

signed main() 
{
	init();
	int T; cin >> T;
	while(T --) solve();
}

H - Travel Begins — 贪心+推导

题意是 给一个数n,任意构造k个实数(可为0),他们的和为n,实数小数部分小于0.5的舍掉,大于等于0.5进位,使其k个数都为整数,求这样做后最小之和 和 最大之和
如果 k > 2 ∗ n k>2*n k>2n可确保 将每个数分为 n / k < 1 / 2 n/k<1/2 n/k<1/2 最小为0, 或者每个数分为0.5,其余为0,最大值为 2 ∗ n 2*n 2n

如果 k < = 2 ∗ n k<=2*n k<=2n 直接贪心即可
最小的时候一定是尽可能让前k-1个数为0.499999999…,这样前k-1个数就会全部把小数舍掉
最大的时候一定是尽可能让前k-1个数为0.5, 这样前k-1个数全都变成1
然后让n减去前k-1个数为第k个数,第k个数小数部分大于0.5就进1,最终求得结果

最小的时候
第k个数为 n − ( 0.5 − e p s ) ∗ ( k − 1 ) n-(0.5-eps)*(k- 1) n(0.5eps)(k1) 其中eps趋近于0
前k-1个数之和为0,只需要判断第k个数是否进位即可
第k个数结果推导
在这里插入图片描述

最大的时候
第k个数为 n − 0.5 ∗ ( k − 1 ) n-0.5*(k-1) n0.5(k1)
前k-1个数之和为k-1
推导
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
void solve()
{
	int n, k; cin >> n >> k;
	if(n * 2 < k) cout << "0 " << n * 2 << '\n';
	else cout << n - (k - 1) / 2 << ' ' << n + k / 2 << '\n';
}

signed main() 
{
	int T; cin >> T;
	while(T --) solve();
}

K - 排列与质数 — 构造

构造题没什么可说的,脑筋急转弯,看官方题解吧
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

void solve() {
	int n; cin >> n;

	if (n <= 4) cout << -1 << "\n";
	else if (n == 5) cout << "4 1 3 5 2\n";
	else if (n == 6) cout << "1 3 5 2 4 6\n";
	else if (n == 7) cout << "1 3 5 7 2 4 6\n";
	else if (n == 8) cout << "1 3 5 7 2 4 6 8\n";
	else if (n == 9) cout << "1 3 5 7 9 2 4 6 8\n";
	else if (n == 10) cout << "1 3 10 5 7 9 2 4 6 8\n";
	else if (n == 11) cout << "1 3 10 5 2 7 9 11 8 6 4\n";
	else
	{
		vector<int> vec;
		if(n & 1) {
			for(int i = 1; i <= n; i += 2)
			{	
				vec.push_back(i);
				if(i == 5) vec.push_back(2);
				if(i == n - 6) vec.push_back(n - 1);
			}	
			for(int i = n - 3; i >= 4; i -= 2)
				vec.push_back(i);
		}
		else {
			for(int i = 1; i <= n - 3; i += 2)
			{
				vec.push_back(i);
				if(i == 5) vec.push_back(2);
			}
			for(int i = n; i >= 4; i -= 2)
			{
				vec.push_back(i);
				if(i == n - 4) vec.push_back(n - 1);
			}
		}
		for(auto x : vec) cout << x << " ";
		cout << '\n';
	}

}
signed main() 
{
	int T; T = 1;
	while(T --) solve(); 
}
  • 23
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_WAWA鱼_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值