2022蓝桥杯省赛CB组做题记录

上午刚打完,下面放的 全部都是考场代码(除了I题是之后补的,考的时候 漏看一个条件)。下面讲讲大致的做法

A

直接模拟

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
#include <cstring>
#include <iostream>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

int main()
{
	
	int res = 0;
	string s;
	cin >> s;
	int jiu = 1;
	for(int i = (int)s.size() - 1;i >= 0;i --)
	{
		res += jiu * (s[i] - '0');
		jiu *= 9;
	}
	printf("%d\n",res);
	return 0;
}

B

题意有歧义。还是模拟。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
#include <iostream>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

bool ck(string str)
{
	for(int i = 0;i + 2 < (int)str.size();i ++)
	{
		if(str[i] > '0' && str[i + 1] == str[i] + 1 && str[i + 2] == str[i] + 2)return true;
		//if(str[i + 1] == str[i] - 1 && str[i + 2] == str[i] - 2)return true;
	}
	return false;
}
string year = "2022";
string toStr(int x)
{
	string rt = "";
	while(x)
	{
		rt.push_back(x % 10 + '0');
		x /= 10;
	}
	while(rt.size() < 2)rt.push_back(0 + '0');
	reverse(rt.begin(),rt.end());
	return rt;
}
int cnt[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; 
int main()
{
	int ans = 0;
	for(int i = 1;i <= 12;i ++)
	{
		for(int j = 1;j <= cnt[i];j ++)
		{
			string month = toStr(i);
			string tmp = year + month + toStr(j);
			if(ck(tmp))
			{
				ans ++;
				cout << tmp << "\n";
			}
			//cout << tmp << "\n";
		}
	}	
	printf("ans = %d",ans);
	return 0;
}

C

直接算天数。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

int main()
{
	ll a,b,n;
	read(a),read(b),read(n);
	ll week = n / (5 * a + 2 * b);
	ll cur = week * (5 * a + 2 * b);
	ll ans = week * 7;
	vector<ll> task = {a,a,a,a,a,b,b,a,a};
	for(int i = 0;i < 7;i ++)
	{
		if(cur >= n)
		{
			break;
		}
		cur += task[i];
		ans ++;
	}
	printf("%lld\n",ans);
	return 0;
}

D

找规律,数列是对称的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

int main()
{
	int n;
	read(n);
	for(int i = 1;i <= n;i ++)
	{
		int x = 2 * (i - 1);
		int y = 2 * (n - i);
		printf("%d\n",max(x,y));
	}
	return 0;
}

E

由于题目保证了 A 不小于 B,因此对每一位的进制,选取合法的最小进制即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

const int N = 1e5 + 55,MOD = 1000000007;

ll a[N],b[N],p[N],mp[N];
int n;
int ma,mb;

int main()
{
	read(n);
	read(ma);
	for(int i = ma - 1;i >= 0;i --)read(a[i]);
	read(mb);
	for(int i = mb - 1;i >= 0;i --)read(b[i]);
	for(int i = ma - 1;i >= 0;i --)p[i] = max(max(a[i],b[i]) + 1,2LL);
	mp[0] = p[0];
	for(int i = 1;i < N - 10;i ++)mp[i] = mp[i - 1] * p[i] % MOD;

	ll res = 0;
	for(int i = ma - 1;i >= 0;i --)
	{
		ll ps;
		if(i == 0)ps = 1;
		else ps = mp[i - 1];
		ps = ps * (a[i] - b[i]) % MOD;
		res = (res + ps) % MOD;
		res = (res % MOD + MOD) % MOD;
	}
	printf("%lld\n",res);
	
	return 0;

}

F

二维前缀和,枚举左上角顶点,再枚举另一条水平线 或 竖直线,由于所有值都非负,因此可以二分答案 ,n3logn, 可能会t。

也可以考虑 枚举矩形的两条边,再对另外两条边 运用双指针算法, n3 肯定不会t。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

const int N = 505;

int n,m,K;
int a[N][N];
int sum[N][N];
inline int getSum(int i,int j,int x,int y)
{
	if(i > x || j > y)return 0;
	return sum[x][y] - sum[x][j - 1] - sum[i - 1][y] + sum[i - 1][j - 1];
}
int main()
{
	read(n),read(m),read(K);
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			read(a[i][j]);
		}
	}
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + a[i][j];
		}
	}
	ll ans = 0;
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			for(int k = i;k <= n;k ++)
			{
				int l = j - 1,r = m;
				while(l < r)
				{
					int mid = (l + r + 1) >> 1;
					int sum = getSum(i,j,k,mid);
					if(sum <= K)l = mid;
					else r = mid - 1;
				}
				ans += 1LL * (l - j + 1);
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}

G,洛谷P1990原题,我分了大概六种情况,维护a,b,c,d,e,f的递推关系,时间复杂度和空间复杂度都是O(6n),n 约为 1e7,刚好卡过。(交洛谷上测试之后,实际使用内存大概230MB)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

//-------------------------
const int MOD = 1000000007;
const int N = 10000005; 
int a[N],b[N],c[N],d[N],e[N],f[N];
signed main()
{	

		
	int n;
	read(n);
	
	a[1] = 1;
	a[2] = 1,b[2] = 0,c[2] = 2,d[2] = 1;
	a[3] = 2,b[3] = 2,c[3] = 2,d[3] = 1,e[3] = 0,f[3] = 2;
	for(int i = 4;i <= n; i++)
	{
		a[i] = ((ll)a[i - 1] + b[i - 1] + d[i - 1]) % MOD;
		b[i] = ((ll)c[i - 1] + e[i - 1] + f[i - 1]) % MOD;
		c[i] = (2 * ((ll)a[i - 2] + b[i - 2] + d[i - 2])) % MOD;
		d[i] = ((ll)a[i - 2] + b[i - 2] + d[i - 2]) % MOD;
		e[i] = ((ll)c[i - 2] + e[i - 2] + f[i - 2]) % MOD;
		f[i] = (2 * ((ll)a[i - 3] + b[i - 3] + d[i - 3])) % MOD;
	}
	int ans = ((ll)a[n] + b[n] + d[n]) % MOD;
	ans = (ans % MOD + MOD) % MOD;
	printf("%d\n",ans);
	
	return 0;
}

H

题目有歧义,没规定火箭的发射顺序(这个我考试的时候没有考虑,直接默认所有火箭按编号顺序发射),那么直接dfs即可,用一个map维护剩余炸弹,枚举每个炸弹 or 火箭 辐射范围内的 所有炸弹,时间复杂度O(r3(n + m)logn)可能会 t 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
#include <map>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<ll,ll> PII;
typedef pair<pair<ll,ll>,ll> PIII;
//距离注意开 long long 
map<PIII,int> cnt;
int n,m;
inline ll dis2(ll x,ll y,ll ox,ll oy)
{
	return (x - ox) * (x - ox) + (y - oy) * (y - oy);
}
int ans = 0;

void dfs(ll x,ll y,ll r)
{
	if(cnt[{{x,y},r}] == 0)return ;
	ans += cnt[{{x,y},r}];
	cnt[{{x,y},r}] = 0;
	for(int i = x - r;i <= x + r;i ++)
	{
		for(int j = y - r;j <= y + r;j ++)
		{
			if(dis2(i,j,x,y) > r * r)continue;
			for(int k = 0;k <= r;k ++)
				dfs(i,j,k);
		}
	}
}

inline void solve(ll x,ll y,ll r)
{
	for(int i = x - r;i <= x + r;i ++)
	{
		for(int j  = y - r;j <= y + r;j ++)
		{
			if(dis2(i,j,x,y) > r * r)continue;	
			for(int k = 0;k <= r;k ++)
			{		
				dfs(i,j,k);
			}
		}
	}	
}

int main()
{
	read(n),read(m);
	for(int i = 1;i <= n;i ++)
	{
		ll x,y,r;
		read(x),read(y),read(r);
		cnt[{{x,y},r}] ++;
	}
	for(int i = 1;i <= m;i ++)
	{
		ll x,y,r;
		read(x),read(y),read(r);
		solve(x,y,r);
	}
	printf("%d\n",ans);
	return 0;
}




I

一开始没有发现 所有的酒都要喝完 这个条件,卡了好久,越想越复杂(因为酒量可能非常大),

考试结束前10分钟,写了个暴力才发现题目看错了(样例都没过)。

正解是dp:

4维 dp(i, state, j, k)

i:当前第i个地方

state:0 or 1 当前这个地方是喝酒(0)还是买酒(1)

j,当前这个地方,身上还剩余的酒量

k,走到当前这个地方,已经买了  k 次酒;

由于最后要喝完,因此 j 不大于 m,

直接dp即可(注意转移顺序)

注意边界;

答案是f[n + m][ 0 ][ 0 ][ n ]。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

const int MOD = 1e9 + 7,N = 209;
int n,m;

ll f[N][2][N][N];

int main()
{
	read(n),read(m);
	f[1][0][1][0] = 1,f[1][1][4][1] = 1;
	for(int i = 2;i <= n + m;i ++)
	{
		for(int j = m + 1;j >= 0;j --)
		{
			for(int k = 1;k <= n;k ++)
				f[i][0][j][k] = (f[i - 1][0][j + 1][k] + f[i - 1][1][j + 1][k]) % MOD;
		}
		for(int j = 0;j <= m + 1;j ++)
		{
			for(int k = 1;k <= n;k ++)
				if(j % 2 == 0)f[i][1][j][k] = (f[i - 1][0][j / 2][k - 1] + f[i - 1][1][j / 2][k - 1]) % MOD;
		}
	}
	printf("%lld\n",f[n + m][0][0][n]);
	return 0;
}

I(考场代码)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

int n,m;

ll ans = 0;

bool ck(int x)
{
	vector<int> a(m + n,0);
	for(int i = 0;i < m + n;i ++)
	{
		a[i] = x % 2;
		x >>= 1;
	}
	if(a[m + n - 1] == 1)return false;
	ll cur = 2;
	int num0 = 0;
	for(int i = 0;i < m + n;i ++)
	{
		num0 += a[i] == 0;
	} 
	if(num0 != m)return false;
	for(int i = 0;i < m + n;i ++)
	{
		if(a[i] == 0)cur --;
		else cur *= 2LL;
		if(cur < 0)return false;
	}
	if(cur != 0)return false; 
	/*
	for(auto p:a)printf("%d",p);
	puts("");
	*/
	return true;
}

int main()
{
	read(n),read(m);
	for(int i = 0;i < 1 << (n + m);i ++)
	{
		if(ck(i))ans++; 
	}
	printf("%lld\n",ans);
	return 0;
}

J

很长时间都没写的数据结构题。

做法很多,这里写了一个维护最值 和 区间修改的线段树。

首先 开根号操作是非常快的,1e18 变成1 需要的次数大概不超过10次。

其次,每次肯定是选 全是最大值的 某一段进行开根号,

因此我们只需要做到(1)维护区间修改(2)查询哪一段是区间最大值

(1)容易维护

(2)等价于求所有 最大值连续段 的端点,设整个区间[1,n]最大值是ma,

先从左到右找出第一个L满足max(1,L) = ma,L为左端点。

再从L开始从左向右找出最后一个R满足min(L,R) = ma,R为右端点

那么[L,R]就是符合要求的一段。

由于总的修改次数 正比于 n,因此二分找区间的次数也正比于n

因此时间复杂度为 O(nlognlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;
template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	for(;!isdigit(c);c = getchar())if(c == '-')f = -f;
	for(; isdigit(c);c = getchar())x = 10 * x + c - '0';
	x *= f;
}
typedef long long ll;
typedef pair<int,int> PII;

const int N = 2e5 + 550;
const ll INF = 4e18;
struct Node
{
	int l,r;
	ll ma;
	ll mi; 
	ll add;
}tr[N * 4];
#define ls(x) x << 1
#define rs(x) x << 1 | 1
int n;
ll h[N];
inline void pushup(int u)
{
	tr[u].ma = max(tr[ls(u)].ma,tr[rs(u)].ma);
	tr[u].mi = min(tr[ls(u)].mi,tr[rs(u)].mi);
}

void build(int u,int l,int r)
{
	tr[u].l = l,tr[u].r = r;
	if(l == r) return ;
	int mid = (l + r) >> 1;
	build(ls(u),l,mid),build(rs(u),mid + 1,r);
}
inline void makeAdd(int u,ll c)
{
	tr[u].ma += c;
	tr[u].add += c;
	tr[u].mi += c;
}
inline void pushdown(int u)
{
	if(tr[u].add == 0)return ;
	makeAdd(ls(u),tr[u].add);
	makeAdd(rs(u),tr[u].add);
	tr[u].add = 0;
}

void modify(int u,int ql,int qr,ll c)// +c
{
	if(ql > qr)return ;
	if(tr[u].l >= ql && tr[u].r <= qr)
	{
		makeAdd(u,c);
		return ;
	}
	pushdown(u);
	int mid = (tr[u].l + tr[u].r) >> 1;
	if(ql <= mid)modify(ls(u),ql,qr,c);
	if(qr >  mid)modify(rs(u),ql,qr,c);
	pushup(u);
}
ll querymax(int u,int ql,int qr)
{
	if(ql > qr)return 0;
	if(tr[u].l >= ql && tr[u].r <= qr)return tr[u].ma;
	pushdown(u);
	int mid = (tr[u].l + tr[u].r) >> 1;
	ll rt = 0;
	if(ql <= mid)rt = max(rt,querymax(ls(u),ql,qr));
	if(qr >  mid)rt = max(rt,querymax(rs(u),ql,qr));
	return rt;
}
ll querymin(int u,int ql,int qr)
{
	if(ql > qr)return INF;
	if(tr[u].l >= ql && tr[u].r <= qr)return tr[u].mi;
	pushdown(u);
	int mid = (tr[u].l + tr[u].r) >> 1;
	ll rt = INF;
	if(ql <= mid)rt = min(rt,querymin(ls(u),ql,qr));
	if(qr >  mid)rt = min(rt,querymin(rs(u),ql,qr));
	return rt;
}
#include <cmath>
inline ll f(ll x)
{
	return (ll)sqrt(x / 2 + 1);
}

int ans = 0;

void solve()
{
	int p = 0;
	ll ma = querymax(1,1,n);
	
	ll tt = f(ma);
	ll delta = tt - ma;
	 
	for(;p <= n;)
	{ 
		if(querymax(1,1,n) < ma)break; 
		int l = p + 1,r = n + 1;
		if(l > n)break; 
		while(l < r)
		{ 
			int mid = (l + r) >> 1;
			if(querymax(1,p + 1, mid) == ma)r = mid;
			else l = mid + 1;
		}
		
		int L = l;//L 第一个最大值为ma的点
		if(L == n + 1)continue;
		
		l = L,r = n + 1;
		while(l < r)
		{ 
			int mid = (l + r + 1) >> 1;
			if(querymin(1,L,mid) != ma)r = mid - 1;
			else l = mid;
		}
		int R = l;//第一个最小值不是ma的点
		if(R == n + 1)continue;
		if(R < L)continue; 
		modify(1,L,R,delta);		 
		ans ++;
		p = R; 
		 
	}
}

int main()
{	

	read(n);
	
	for(int i = 1;i <= n;i ++)read(h[i]);
	build(1,1,n + 1);
	for(int i = 1;i <= n;i ++)modify(1,i,i,h[i]);
	
	
	while(querymax(1,1,n) > 1)
	{
		solve();
	
	}
	printf("%d\n",ans);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值