4.28~5.4

蓝桥杯15届国赛B

题目可以在官网https://www.lanqiao.cn/上查到

填空A

暴力找每个长度为8~16的子区间,然后判断是否满足条件即可

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	string s = " kfdhtshmrw4nxg#f44ehlbn33ccto#mwfn2waebry#3qd1ubwyhcyuavuajb#vyecsycuzsmwp31ipzah#catatja3kaqbcss2th";
	int si = s.size()-1, sum = 0;
	for(int i=1;i<=si;i++){
		bool num = 0, fu = 0;
		for(int j=i;j<=i+15 && j<=si;j++){
			if(s[j] - '0' >= 0 && s[j] - '0' <= 9) num = 1;
			else if(s[j] == '#')fu = 1;
			if(j >= i+7 && num && fu) sum++;
		}
	}
	cout << sum << '\n';
	return 0;
}

填空B

数学题,用数学方法做

可以发现,分母是一样的,令分子为分别s1,s2,s3,可以算出,a🅱️c = s1s3 : s1s2 : s2*s3,令d为abc比值的最大公约数,求出满足a🅱️c的最小整数

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int s1 = 2585, s2 = 2632, s3 = 1540;
	int d = __gcd(s1*s3,__gcd(s1*s2,s2*s3));
	cout << s1*s3/d << ',' << s1*s2/d << ',' << s2*s3/d << '\n';
	return 0;
}

C

题目数据量大,有一个5e7的方法可以过:

枚举每一条线段上的整数点,用哈希表来存点,如果这个点的统计次数>=2,那么就表示可以设为会议地点

怎么枚举每一条线段的整数点呢?

当这条线段平行于坐标轴时,x轴增量就是(x2 > x1) - (x1 > x2),y轴增量就是(y2 > y1) - (y1 > y2)

否则,x轴增量就是(x2-x1)/g,其中g是Δx和Δy的最大公约数;y轴增量同理

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;

map <int,int> hash_[maxn];

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n, ans = 0;
	cin >> n;
	while(n--){
		int x1,x2,y1,y2;
		cin >> x1 >> y1 >> x2 >> y2;
		int dex = abs(x2-x1), dey = abs(y2-y1);
		int g = __gcd(dex,dey);
		if(x1 == x2 || y1 == y2){
			int stx = (x2 > x1) - (x1 > x2), sty = (y2 > y1) - (y1 > y2);
			while(x1 != x2 || y1 != y2){
				hash_[x1][y1]++;
				if(hash_[x1][y1] == 2) ans++;
				x1 += stx;
				y1 += sty;
			}
			if(++hash_[x2][y2] == 2) ans++;
		}
		else{
			int stx = (x2-x1)/g, sty = (y2-y1)/g;
			while(x1 != x2 || y1 != y2){
				hash_[x1][y1]++;
				if(hash_[x1][y1] == 2) ans++;
				x1 += stx;
				y1 += sty;
			}
			if(++hash_[x2][y2] == 2) ans++;
		}
	}
	cout << ans << '\n';
	return 0;
}

D

二分找最小的最长距离L

每个点之间增加的点的数量是ceil(dis/L)-1,dis是相邻两点间的距离

使用技能可以看成是多了一个增加点,也就是增加的点的总数变为m+1

对于每一个二分找的L,用检查函数来检查增加点的总数cnt是否小于等于m+1

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;

int a[maxn], n, m;

bool check(int L){
	int cnt = 0;
	for(int i=1;i<=n;i++){
		cnt += ceil((1.0*a[i] - 1.0*a[i-1]) / L) - 1;
		//cout << "L: " << L << " jia: " << ceil((1.0*a[i] - 1.0*a[i-1]) / L) - 1 << '\n';
	}
	return cnt <= m+1;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	for(int i=1;i<=n;i++) cin >> a[i];
	a[0] = 0;
	int l = 1, r = a[n];
	while(l < r){
		int mid = (l+r) >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l << '\n';
	return 0;
}

E

贪心+排序

这道看似简单的题我交了6发......,只能说坑多

首先,字典序最小就是一个坑,要使得字典序最小,那么对于s2的第i个字符,要在s1里找到第一个ASCⅡ码严格大于它的字符,也就是说在s2和s1的某个字符相等的情况下,要优先输出s1的字符

然后注意边界问题

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m;
	string s1,s2;
	cin >> n >> m >> s1 >> s2;
	int p1 = 0, p2 = 0;
	sort(s2.begin(),s2.end());
	while(p1<=n || p2<=m){
		if(p1 == n && p2 == m) break; 
		if(p1 == n) cout << s2[p2++];
		else if(p2 == m) cout << s1[p1++];
		else if(s1[p1] > s2[p2]) cout << s2[p2++];
		else cout << s1[p1++];
	}
	cout << '\n';
	return 0;
}

F

题目标签是DP,题解也是清一色的dp解法,没有其他解法的题解,BUT ! 这道题明显贪心更简单呐?题解里居然还有人说贪心难写 bug多,NO!按我这么贪 一遍就过

准备两个数组 a 和 ra ,a存输入的原始数据,ra存原始数据的二进制反转数据

再准备一个数组存每个ra[i] > a[i]的区间,这里我用vector来存

贪心思想,存完vector数组后降序排序,取前min(m,vec.size())个数据就可以算出最大的增加值,这个增加值加上原来的总和就是答案了

#include <bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn = 1e3+5;

int a[maxn],ra[maxn],n,m;
struct node{
	int add,l,r;
};
vector <node> vec;

bool cpa(node a,node b){
	return a.add > b.add;
}

void rev(int a,int i){
	queue <int> q;
	while(a){
		int yu = a%2;
		a /= 2;
		q.push(yu);
	}
	int num = 0;
	while(!q.empty()){
		num += q.front() * pow(2,q.size()-1);
		q.pop();
	}
	ra[i] = num;
	return;
}

int f(int index){
	int sum = 0;
	for(int i=index;i<=n;i++){
		if(ra[i] <= a[i]){
			vec.push_back({sum,index,i-1});
			return i-1;
		}
		sum += ra[i] - a[i];
	}
	vec.push_back({sum,index,n});
	return n;
}

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	int sum = 0;
	for(int i=1;i<=n;i++){
		cin >> a[i];
		sum += a[i];
		rev(a[i],i);
	}
	for(int i=1;i<=n;i++){
		if(ra[i] > a[i]){
			i = f(i);
		}
	}
	sort(vec.begin(),vec.end(),cpa);
	int si = vec.size();
	for(int i=0;i<si && i<m;i++){
		sum += vec[i].add;
	}
	cout << sum << '\n';
	return 0;
}

牛客月赛114

题目链接:https://ac.nowcoder.com/acm/contest/106859

哪个杀批出的题,我这就去给三国杀刷好评,定叫你好评如潮

A

签到

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n;
	cin >> n;
	for(int i=1;i<=n;i++){
		int a;
		cin >> a;
		if(i == 1) cout << (a>1? (a+1<=5 ? a+1:5):2) << ' ';
		else cout << (a>1? (a-1<=5 ? a-1:5):0) << ' ';
	}
	cout << '\n';
	return 0;
}

B

这场赛居然有两道签到题...看来后面的题比较难了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+1;

bool vis[maxn];

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n;
	cin >> n;
	for(int i=0;i<n;i++){
		int cache;
		cin >> cache;
		vis[cache] = 1;
	}
	long long sum = 0;
	for(int i=1;i<=n;i++) if(vis[i]) sum += (long long)i;
	cout << sum << '\n';
	return 0;
}

C

大致题意就是给定一张图,让你找出这个图里有没有环

邻接表存图,然后bfs搜

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+1;

int n, x;
bool vis[maxn];
vector <int> e[maxn];

void bfs(){
	queue <int> q;
	q.push(x);
	vis[x] = 1;
	while(!q.empty()){
		int start = q.front();
		q.pop();
		int si = e[start].size();
		for(int i=0;i<si;i++){
			if(vis[e[start][i]]){
				cout << "Yes\n";
				return;
			}
			q.push(e[start][i]);
			vis[e[start][i]] = 1;
		}
	}
	cout << "No\n";
	return;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> x;
	for(int i=1;i<=n;i++){
		int f;
		cin >> f;
		e[f].push_back(i);
	}
	bfs();
	return 0;
}

D

初看时考虑用dp,但是不好做。这种从队头队尾摸东西的题以前碰到过,比较常见的解法是做一个最大前后缀和,然后遍历一遍求最大和

总摸牌数是可以先计算出来的,记为t

遍历一遍,枚举前面拿x张,后面拿n-x张的最大值

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5+5;

int a[maxn],pre[maxn],hou[maxn];

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	for(int i=1;i<=n;i++) cin >> a[i];
	int m = 0;
	for(int i=1;i<=n;i++){
		m += a[i];
		pre[i] = max(pre[i-1],m);
	}
	m = 0;
	for(int i=n;i>=1;i--){
		m += a[i];
		hou[i] = max(hou[i+1],m);
	}
	int t = 0;
	for(int i=0;i<n;i++){
		if(k<i) break;
		k = k-i+1;
		t++;
	}
	int ans = max(pre[t],hou[n-t+1]);
	for(int i=1;i<t;i++) ans = max(ans,pre[i]+hou[n-(t-i)+1]);
	cout << ans << '\n';
	return 0;
}

剩下的题下周来,本周51真的玩爽了......

posted @ 2025-06-14 12:28  HLAIA  阅读(10)  评论(0)    收藏  举报