2023年第十四届蓝桥杯大赛软件类省赛C/C++大学C组真题

困于心,衡于虑,而后作。

打卡第四篇。

A.求和

B.工作时长

#include <bits/stdc++.h>
using namespace std;
 
//2022是平年
int Month[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
 
int sum[520];
int month, day, h, mi, s; //月,日,时,分,秒
 
int main() {
  int ans = 0;
    for (int i = 0; i < 520; i++) {
        string str;
        getline(cin, str);
        month = (str[6] - '0') + (str[5] - '0') * 10;
        day = (str[9] - '0') + (str[8] - '0') * 10;
        h = (str[12] - '0') + (str[11] - '0') * 10;
        mi = (str[15] - '0') + (str[14] - '0') * 10;
        s = (str[18] - '0') + (str[17] - '0') * 10;
        sum[i] = (Month[month - 1] + day) * 86400 + h * 3600 + mi * 60 + s;
    }
    sort(sum, sum + 520);
    for (int i = 0; i < 520; i += 2) {
        ans += sum[i + 1] - sum[i];
    }
    cout << ans << endl;
    return 0;
}

C.三国游戏

真服了,调试半天没过,原来是没看清输入,for(l i=0;i<n;i++)cin>>x[i]>>y[i]>>z[i];无语了...... 

正确代码: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll work(vector<ll> &a,vector<ll> &b,vector<ll> &c,ll &n){
	vector<ll> w(n,0);
	for(ll i=0;i<n;i++){
		w[i]=(a[i]-b[i]-c[i]);
	}
	sort(w.rbegin(),w.rend());
	//for(ll i=0;i<n;i++)cout<<w[i]<<endl;
	//if(w[0]<=0)return -1;
	ll flag=-1,res=0;
	for(ll i=0;i<n;i++){
		res+=w[i];
		if(res>0){
			flag=i+1;
		}
		else break;
	}
	return flag;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	vector<ll> x(n),y(n),z(n);
	for(int i = 0; i < n; i++) cin >> x[i]; 
	for(int i = 0; i < n; i++) cin >> y[i];
	for(int i = 0; i < n; i++) cin >> z[i]; 
	ll MAX=max({work(x,y,z,n),work(z,y,x,n),work(y,x,z,n)});
	cout<<MAX;
	return 0;
}

 D.填充 

错误代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	string s;cin>>s;
	//cout<<s<<endl;
	for(ll i=0;i<s.length()-1;i++){
		if(s[i]=='?'){
			if(i==0){
				if(s[1]=='0')s[i]='0';
				else if(s[1]=='1')s[i]='1';
				else if(s[1]=='?'){
					//?? 0001
					//00 0001
					//11 0001
					//10 0001
					//01 0001
					s[0]='0';s[1]='0';
					//s[0]='1';s[1]='1';
				}
			}else{
				if(s[i+1]=='0')s[i]='0';
				else if(s[i+1]=='1')s[i]='1';
				else if(s[i+1]=='?'){
					//1010??11111
					//10100011111
					s[i]=s[i-1];
//不用担心越界,此时有大前提i>0,也别管s[i+1]=s[i+2],不然遍历范围改变又乱套了
				}
			}
		}
	}
	if(s[s.length()-1]=='?'){
		s[s.length()-1]=s[s.length()-2];
	}
	//cout<<s<<endl;
	ll count=0;
	ll i=1;
	while(i<s.length()){
		if((s[i]=='0'&&s[i-1]=='0')||(s[i]=='1'&&s[i-1]=='1')){
			count++;
			i+=2;
		}else i++;
	}
	cout<<count;
	
	return 0;
}
/*
  1110000????11010101???
  1110000????11010101???
  1110000000111010101111
 */

打表测试发现,第7个样例还是不够严谨。。

  

答案代码:

其实透过问题看本质也就是,四种情况:

1.  00或者11组合,count++,跳转步长为2.

2.  10或者01组合,步长为1,跳到i+1继续遍历查询。

3.  ??组合,??可以是11,也可以是00,爱是谁是谁,count++,i+=2;

4.  1?,?1, 0?, ?0,也就两个字符里面包含?,别管另一个字符到底是1还是0,因为?肯定跟着另一个字符变,爱是谁是谁,count++,i+=2;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	string s;cin>>s;
	ll count=0;
	for(ll i=0;i<s.length()-1;i++){
		if(s[i]==s[i+1]||s[i]=='?'||s[i+1]=='?'){
			count++;
			i+=1;
		}else continue;
	}
	cout<<count;
	return 0;
}

E.翻转 

和填充的思路差不多。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ll n;cin>>n;
	while(n--){
		string t,s;cin>>t>>s;
		ll i=1;
		ll count=0;
		while(i<t.length()-1){
			if(t[i]==s[i])i++;
			else{
				if(i<=t.length()-2&&s[i-1]!=s[i]&&s[i]!=s[i+1]){
					s[i]=t[i];
					count++;
					i+=2;
				}
				else i++;
			}
		}
		//cout<<s<<'\n';
		if(t==s)cout<<count<<'\n';
		else cout<<-1<<'\n';
	}
	return 0;
}

 F.子矩阵

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

static const long long MOD = 998244353;

/*
 * 一维滑窗 - 求区间最大值
 * 输入: arr 原数组, k 窗口大小
 * 返回: 一个 vector, 其长度为 arr.size() - k + 1
 *       第 i 个元素是 arr[i..i+k-1] 的最大值
 */
vector<long long> slidingWindowMax(const vector<long long>& arr, int k) {
    deque<int> dq;  // 存放下标, 保证队首是当前窗口的最大值下标
    vector<long long> res;
    res.reserve(arr.size() - k + 1);

    for (int i = 0; i < (int)arr.size(); i++) {
        // 1) 保证队尾对应的 arr 值 >= 当前元素, 不符合就弹出
        while (!dq.empty() && arr[dq.back()] <= arr[i]) {
            dq.pop_back();
        }
        dq.push_back(i);

        // 2) 窗口已经超过大小 k,移除队首
        if (dq.front() <= i - k) {
            dq.pop_front();
        }

        // 3) 当 i >= k-1 时,窗口 [i-k+1..i] 已形成,队首即最大值
        if (i >= k - 1) {
            res.push_back(arr[dq.front()]);
        }
    }
    return res;
}

/*
 * 一维滑窗 - 求区间最小值
 * 方法与最大值类似,只是比较符号相反
 */
vector<long long> slidingWindowMin(const vector<long long>& arr, int k) {
    deque<int> dq;
    vector<long long> res;
    res.reserve(arr.size() - k + 1);

    for (int i = 0; i < (int)arr.size(); i++) {
        // 弹出所有 >= arr[i] 的元素,以保证队首是最小值
        while (!dq.empty() && arr[dq.back()] >= arr[i]) {
            dq.pop_back();
        }
        dq.push_back(i);

        // 移除不在窗口内的下标
        if (dq.front() <= i - k) {
            dq.pop_front();
        }

        // 当 i >= k-1 时,队首即窗口最小值
        if (i >= k - 1) {
            res.push_back(arr[dq.front()]);
        }
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, a, b;
    cin >> n >> m >> a >> b;  // n 行 m 列, 子矩阵 a 行 b 列

    // 读入矩阵
    vector<vector<long long>> mat(n, vector<long long>(m));
    for (int i = 0; i < n; i++){
        for (int j = 0; j < m; j++){
            cin >> mat[i][j];
        }
    }

    // 第一步: 行方向滑窗, 得到每一行的区间最值 (长 m-b+1)
    // maxRow[r][c]: 第 r 行,从列 c 开始长度为 b 的窗口最大值
    // minRow[r][c]: 第 r 行,从列 c 开始长度为 b 的窗口最小值
    vector<vector<long long>> maxRow(n), minRow(n);
    for (int r = 0; r < n; r++){
        maxRow[r] = slidingWindowMax(mat[r], b); // 大小 (m - b + 1)
        minRow[r] = slidingWindowMin(mat[r], b);
    }

    // 第二步: 再在列方向进行大小为 a 的滑窗
    // 我们有 n 行, 每行 (m - b + 1) 列
    // 对于每一列 c (0 <= c < m-b+1),把 maxRow[r][c] (r=0..n-1) 视为一个数组
    // 在它上做窗口大小 a 的一维滑窗, 结果就是最终 (n-a+1) 行
    // 同理对 minRow 也一样
    // 最终得到的 maxVal[i][c], minVal[i][c] 即对应子矩阵的最大/最小值
    vector<vector<long long>> maxVal(n - a + 1, vector<long long>(m - b + 1));
    vector<vector<long long>> minVal(n - a + 1, vector<long long>(m - b + 1));

    for (int c = 0; c < m - b + 1; c++){
        // 准备一个临时数组, 收集 maxRow[r][c] (r=0..n-1)
        vector<long long> colArrMax(n), colArrMin(n);
        for (int r = 0; r < n; r++){
            colArrMax[r] = maxRow[r][c];
            colArrMin[r] = minRow[r][c];
        }

        // 对 colArrMax / colArrMin 做大小为 a 的滑窗
        vector<long long> colMaxRes = slidingWindowMax(colArrMax, a); // 大小 n-a+1
        vector<long long> colMinRes = slidingWindowMin(colArrMin, a);

        // 将结果写回 maxVal / minVal
        for (int r = 0; r < n - a + 1; r++){
            maxVal[r][c] = colMaxRes[r];
            minVal[r][c] = colMinRes[r];
        }
    }

    // 第三步: 计算所有子矩阵 (a x b) 的价值之和 (max * min),并取模
    long long ans = 0;
    for (int i = 0; i < n - a + 1; i++){
        for (int j = 0; j < m - b + 1; j++){
            // 可能达到 1e9 * 1e9 = 1e18, 在 long long 范围内
            long long val = (maxVal[i][j] % MOD) * (minVal[i][j] % MOD) % MOD;
            ans = (ans + val) % MOD;
        }
    }

    cout << ans << "\n";
    return 0;
}

​
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
struct point{
	ll val;
	int x;
	int y;
	bool operator > (point t) const{
		return val >= t.val;
	}
	bool operator < (point t) const{
		return val <= t.val;
	} 
};
struct num{
	int maxn;
	int minn;
};
const int N = 1010;
ll f[N][N];
num nums[N][N];
int n,m,a,b;
void init(){
	// 对于每一列建立两个优先队列:
	// Q[i] 用来维护最小值(采用 min-heap,即使用 greater<point>)
	// M[i] 用来维护最大值(采用 max-heap,即使用 less<point>)
	priority_queue<point, vector<point>, greater<point>> Q[N];
	priority_queue<point, vector<point>, less<point>> M[N];
	
	// 第一部分:初始化每一列的第一个滑动窗口 [1, a]
	for(int i=1;i<=m;i++){
		for(int j=1;j<=a;j++){
			Q[i].push({f[j][i], i, j});
			M[i].push({f[j][i], i, j});
		} 
		// 记录第一个窗口的最小值和最大值
		nums[1][i].maxn = M[i].top().val;
		nums[1][i].minn = Q[i].top().val;
	} 
	
	// 第二部分:向下滑动窗口,每次增加一行,去掉窗口外的数
	for(int i=1;i<=m;i++){
		for(int j=2;j<=n-a+1;j++){
			// 将新的进入窗口的元素加入队列,位置为 (j+a-1, i)
			Q[i].push({f[j+a-1][i], i, j+a-1});
			M[i].push({f[j+a-1][i], i, j+a-1});
			
			// 若队列顶端的元素不在当前窗口内,则弹出
			while(Q[i].top().y < j){
				Q[i].pop();
			}
			while(M[i].top().y < j){
				M[i].pop();
			}  
			// 更新当前窗口对应的最小值和最大值
			nums[j][i].maxn = M[i].top().val;
			nums[j][i].minn = Q[i].top().val;
		}
	}
}

int main(){
	ll res = 0;
	// 用于水平滑动窗口的两个优先队列:
	// Q 用来维护最小值,采用 min-heap (greater<point>)
	// M 用来维护最大值,采用 max-heap (less<point>)
	priority_queue<point, vector<point>, greater<point>> Q;
	priority_queue<point, vector<point>, less<point>> M;
	
	cin >> n >> m >> a >> b;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin >> f[i][j];
		}
	}
	
	// 预处理:先在垂直方向求出每列的 a 行区间最值
	init();
	
	// 对于每个垂直窗口(行窗口),即从第 i 行开始的 a 行
	for(int i = 1; i <= n-a+1; i++){
		// 初始化水平方向的第一个窗口:前 b 列
		for(int j=1; j<=b; j++){
			Q.push({nums[i][j].minn, j, i});
			M.push({nums[i][j].maxn, j, i});
		}
		// 当前水平窗口对应的子矩阵的最小值为 Q.top().val,最大值为 M.top().val
		res += (Q.top().val * M.top().val) % mod;
		
		// 向右滑动窗口,处理剩余窗口
		for(int j=2; j<=m-b+1; j++){
			// 将新进入窗口的那一列加入队列
			Q.push({nums[i][j+b-1].minn, j+b-1, i});
			M.push({nums[i][j+b-1].maxn, j+b-1, i});
			
			// 弹出那些已经不在当前窗口内的元素(依据存入时记录的列号 x)
			while(Q.top().x < j){
				Q.pop();
			}
			while(M.top().x < j){
				M.pop();
			}
			
			// 累加当前窗口的贡献
			res += (Q.top().val * M.top().val) % mod;
		}
		// 清空两个优先队列,为下一个行窗口作准备
		while(!Q.empty()){
			Q.pop();
		}
		while(!M.empty()){
			M.pop();
		}
	}
	
	cout << res % mod << endl;
	
	return 0;
}

​

G.互质数的个数  

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=998244353;
ll qsm(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=(ans%N*a%N)%N;
		a=(a%N*a%N)%N;
		b>>=1;
	}
	return ans;
}
ll Euler(ll ans){
	ll res=ans;
	for(ll i=2;i<=ans/i;i++){
		if(ans%i==0){
			res=(res%N/i%N*(i-1))%N;
			while(ans%i==0)ans/=i;
		}
	}
	if(ans>1){
		res=res%N/ans%N*(ans-1)%N;
	}
	return (res+N)%N;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll a,b;cin>>a>>b;
	ll ans=qsm(a,b);
	ll res=Euler(ans)%N;
	cout<<res;
	return 0;
}

H.异或和之差 

 

字典树。。以后学树专题了再来补题。

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std ;
const int N = 1e6 + 10 ; 
int  n , a[N] ; 
int son[2][N] , idx ; 
int mx[N] , mi[N] ;
void add(int x)
{
    int p = 0 ; 
    for(int i = 20 ; i >= 0 ; i --)
    {
        int u = (x>>i) & 1; //判断x的第i位上是1/0
        if(!son[u][p]) son[u][p] = ++idx;   //若节点不存在,则按照当前位置上的数是多少来构造树
        p = son[u][p];  //指向构造好的节点准备构造下一位
    }
}
int query_mx(int x)
{//异或值最大则需要尽可能多【不相同】的位数
    int p = 0 , res = 0;
    for(int i = 20;i >= 0;i --)
    {
        int u = (x >> i) & 1;
        if(son[!u][p])  res |= (1 << i);
        else u = !u;
        p = son[!u][p];
    }
    return res;
}
int query_mi(int x)
{//异或值最小则需要尽可能多【相同】的位数
    int p = 0 , res = 0;
    for(int i = 20;i >= 0;i --)
    {
        int u = (x >> i) & 1;
        if(!son[u][p])  //若与当前位相同的节点不存在,则最小异或
        {
            u = !u;
            res |= (1 << i);
        }
        p = son[u][p];
    }
    return res;
}
int main()
{
    cin>>n;
    for(int i = 1;i <= n;i ++)  cin>>a[i];
    mx[0] = 0 , mi[0] = 1e9;
    int sum = 0;
    add(sum);
    for(int i = 1;i <= n;i ++)
    {//寻找i左边的异或和最大值和最小值
        sum ^= a[i];    //前缀和
        mx[i] = max(mx[i-1] , query_mx(sum));    //动态规划,i左边的最大值=i-1左边的最大值(不包括i)和以i为右端点(包括i)的最大值
        mi[i] = min(mi[i-1] , query_mi(sum));
        add(sum);    //构造树
    }
    memset(son , 0 , sizeof son) ; 
    idx = 0 ; sum = 0 ;
    int ans = 0 , mx2 = 0 , mi2 = 2e9; 
    add(sum) ; 
    for(int i = n ; i ; i --)
    {//寻找i右边的异或和最大值和最小值(倒序)
        sum ^= a[i] ; 
        mx2 = max(mx2 , query_mx(sum)) ; //同上
        mi2 = min(mi2 , query_mi(sum)) ; 
        //取(ans , 右最大-左最小 , 左最大-右最小)的最大值
        ans = max({ans , mx[i - 1] - mi2 , mx2 - mi[i - 1]}) ; 
        add(sum) ; 
    }
    cout<<ans<<endl;
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

// -----------------------------------------------------
// 位Trie,用于维护一批整数的二进制表示,支持:
//   insert(x) :向Trie插入一个数 x
//   queryMax(x):在Trie现有数的集合中,与 x XOR 最大的值
//   queryMin(x):在Trie现有数的集合中,与 x XOR 最小的值
// 题目给定 Ai ≤ 2^20,因此我们只需 21 位 (0~20)。
// -----------------------------------------------------
static const int BIT_SIZE = 21; // 针对 0 <= A[i] <= 2^20
struct XORTrie {
    // trie[u][0 or 1] 存储节点 u 的儿子在位 0 / 1 的索引, -1 表示无
    vector<array<int,2>> trie;
    // 记录该节点下有多少前缀(可不需要计数也行, 但有时要判空)
    
    // 构造时给定一个比较大的初始容量
    XORTrie() { 
        trie.push_back({-1, -1}); 
    }
    
    // 向 Trie 中插入一个数 x
    void insert(unsigned x) {
        int u = 0; // 根
        for(int b = BIT_SIZE - 1; b >= 0; b--){
            // 取到 x 在第 b 位的值
            int bit = (x >> b) & 1;
            // 若没有这条边,就创建
            if(trie[u][bit] < 0){
                trie[u][bit] = trie.size();
                trie.push_back({-1, -1});
            }
            u = trie[u][bit];
        }
    }
    
    // 在 Trie 中查找与 x 做 XOR 后能得到的最大值
    unsigned queryMax(unsigned x){
        int u = 0;
        unsigned ret = 0;
        for(int b = BIT_SIZE - 1; b >= 0; b--){
            int bit = (x >> b) & 1;
            // 贪心:如果能走到与 bit 相反的分支,会得到更大异或
            int go = bit ^ 1; 
            if(trie[u][go] < 0) {
                go = bit; // 只能走相同的
            }
            u = trie[u][go];
            // go != bit说明我们得到了更高位的 1
            ret = (ret << 1) | (go ^ bit); 
        }
        return ret;
    }
    
    // 在 Trie 中查找与 x 做 XOR 后能得到的最小值
    unsigned queryMin(unsigned x){
        int u = 0;
        unsigned ret = 0;
        for(int b = BIT_SIZE - 1; b >= 0; b--){
            int bit = (x >> b) & 1;
            // 贪心:如果能走到和 bit 相同的分支,会得到更小异或
            int go = bit; 
            if(trie[u][go] < 0) {
                go = bit ^ 1; // 不得不走相反的
            }
            u = trie[u][go];
            ret = (ret << 1) | (go ^ bit);
        }
        return ret;
    }
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<unsigned> A(n+1);
    for(int i=1; i<=n; i++){
        cin >> A[i];
    }

    // ------------------------
    // 1) 从左到右:prefix[i] = A[1] XOR A[2] XOR ... XOR A[i]
    //    求出在 [1..i] 上能取到的子段异或的最大值 & 最小值
    // ------------------------
    vector<unsigned> prefix(n+1, 0U);
    for(int i = 1; i <= n; i++){
        prefix[i] = prefix[i-1] ^ A[i];
    }
    
    // leftMax[i]:在区间 [1..i] 上可取到的子段异或最大值
    // leftMin[i]:在区间 [1..i] 上可取到的子段异或最小值
    vector<unsigned> leftMax(n+1), leftMin(n+1);
    
    // 初始化
    leftMax[0] = 0;       // [1..0]无效,可当作 0
    leftMin[0] = 0;       // 同理
    XORTrie trieMaxL, trieMinL;
    trieMaxL.insert(0);   // 插入 prefix[0] = 0
    trieMinL.insert(0);
    
    unsigned curMax = 0, curMin = 0; // 记录当前能取到的最大/最小子段异或
    for(int i = 1; i <= n; i++){
        // 以 prefix[i] 结尾的子段 XOR 可能达到的极大值
        unsigned bestMax = trieMaxL.queryMax(prefix[i]);
        curMax = max(curMax, bestMax);
        
        // 以 prefix[i] 结尾的子段 XOR 可能达到的极小值
        unsigned bestMin = trieMinL.queryMin(prefix[i]);
        // 因为一开始是0,先赋值一下
        if(i==1) curMin = bestMin;
        else     curMin = min(curMin, bestMin);
        
        leftMax[i] = max(leftMax[i-1], curMax);
        leftMin[i] = (i==1 ? bestMin : min(leftMin[i-1], curMin));
        
        // 将 prefix[i] 插入 Trie
        trieMaxL.insert(prefix[i]);
        trieMinL.insert(prefix[i]);
    }
    
    // ------------------------
    // 2) 从右到左:suffix[i] = A[i] XOR A[i+1] XOR ... XOR A[n]
    //    求出在 [i..n] 上能取到的子段异或最大值 & 最小值
    // ------------------------
    vector<unsigned> suffix(n+2, 0U);
    // suffix[n+1]=0
    for(int i = n; i >= 1; i--){
        suffix[i] = suffix[i+1] ^ A[i];
    }
    vector<unsigned> rightMax(n+2), rightMin(n+2);
    
    XORTrie trieMaxR, trieMinR;
    trieMaxR.insert(0);
    trieMinR.insert(0);
    
    unsigned curMaxR=0, curMinR=0;
    // 这里从 n 往 1 遍历
    for(int i=n; i>=1; i--){
        // 以 suffix[i] 结尾(从右数)的子段异或极大值
        unsigned bestMax = trieMaxR.queryMax(suffix[i]);
        curMaxR = max(curMaxR, bestMax);
        
        // 以 suffix[i] 结尾(从右数)的子段异或极小值
        unsigned bestMin = trieMinR.queryMin(suffix[i]);
        if(i==n) curMinR = bestMin;
        else     curMinR = min(curMinR, bestMin);
        
        rightMax[i] = (i==n ? bestMax : max(rightMax[i+1], curMaxR));
        rightMin[i] = (i==n ? bestMin : min(rightMin[i+1], curMinR));
        
        // 插入
        trieMaxR.insert(suffix[i]);
        trieMinR.insert(suffix[i]);
    }
    
    // ------------------------
    // 3) 枚举分割点 i,左右子段分别落在 [1..i] 和 [i+1..n]
    //    差值为:
    //       leftMax[i]  - rightMin[i+1]
    //    或
    //       rightMax[i+1] - leftMin[i]
    // ------------------------
    long long ans = LLONG_MIN; 
    // 用 long long 避免大数减法错误
    
    for(int i=1; i < n; i++){
        // 两种方案
        long long cand1 = (long long)leftMax[i]  - (long long)rightMin[i+1];
        long long cand2 = (long long)rightMax[i+1] - (long long)leftMin[i];
        ans = max(ans, max(cand1, cand2));
    }
    
    cout << ans << "\n";
    return 0;
}

I.公因数匹配 

 

暴力:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	vector<ll> a(n+1,0);
	for(ll i=1;i<=n;i++)cin>>a[i];
	for(ll i=1;i<=n;i++){
		for(ll j=i+1;j<=n;j++){
			if(__gcd(a[i],a[j])>1){
				cout<<i<<" "<<j;
				return 0;
			}
		}
	}
	return 0;
}

 答案:

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

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    // 用 unordered_map 存储因子 -> 该因子第一次出现的位置(1-based)
    unordered_map<int,int> mp;

    int minIndex = 99999999, maxIndex = 0; // 最终答案,初值和 Java 代码一致
    int r = minIndex, l = 0;  // 用于记录临时的较早位置 r 与当前位置 l

    for (int i = 0; i < n; i++){
        int a;
        cin >> a;
        // k 取 sqrt(a)+2,以防出现精度问题或遗漏因子
        int k = (int)(sqrt(a) + 2);
        // 枚举因子 j,从 2 到 k
        for (int j = 2; j <= k; j++){
            if (a % j == 0 && a != j) { // 如果 j 是 a 的因子,并且 a != j
                // 处理因子 j
                if (mp.find(j) == mp.end()){
                    mp[j] = i + 1;  // 若 j 不存在,则存入当前下标 (1-based)
                } else if (mp[j] != i + 1){
                    r = min(r, mp[j]);
                    l = i + 1;
                }
                // 同时 a/j 也是 a 的一个因子
                int factor = a / j;
                if (mp.find(factor) == mp.end()){
                    mp[factor] = i + 1;
                } else if (mp[factor] != i + 1){
                    r = min(r, mp[factor]);
                    l = i + 1;
                }
            }
        }
        // 处理 a 本身(因为一个数的因子中也包括它自身)
        if (mp.find(a) == mp.end()){
            mp[a] = i + 1;
        } else if (a != 1 && mp[a] != i + 1){
            r = min(r, mp[a]);
            l = i + 1;
        }
        // 如果本次出现有更新,则记录答案
        if (r != minIndex) {
            minIndex = r;
            maxIndex = l;
        }
    }
    cout << minIndex << " " << maxIndex << "\n";
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+1;
//埃拉托色尼筛法筛选出前N个数中的所有素数,方便提高自因数分解的效率
vector<ll> primes;
void sieve(){
	vector<bool> is_prime(N,true);
	is_prime[0]=is_prime[1]=false;
	
	for(ll i=2;i*i<N;i++){
		if(is_prime[i]){
			for(ll j=i*i;j<N;j+=i){
				is_prime[j]=false;
			}
		}
	}
	
	for(ll i=2;i<N;i++){
		if(is_prime[i]){
			primes.push_back(i);
		}
	}
}

void fenjie(ll x,vector<ll> &fac){
	//如果是素数,其因子只有1和他本身,不存1,不然有影响
	if(find(primes.begin(),primes.end(),x)!=primes.end()){
		fac.push_back(x);
	}else{
		ll cur=x;
		for(ll i=2;i<=cur/i;i++){
			if(cur%i==0){
				fac.push_back(i);
				while(cur%i==0)cur/=i;
			}
		}
		if(cur>1)fac.push_back(cur);
	}
}

int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	vector<ll> a(n+1,0);//存输入的数
	vector<vector<ll>> mp(n+1);//存每个数,还有这个数的所有除了1的质因子
	for(ll i=1;i<=n;i++){
		cin>>a[i];
		fenjie(a[i],mp[i]);
	}
	
	sieve();
	
	vector<ll> pos(N,0);//存数字第一次出现的位置,
	ll best_i=n+1,best_j=n+1;
	for(ll i=1;i<=n;i++){
		
		for(ll j=0;j<mp[i].size();j++){
			
			ll p=mp[i][j];
			if(pos[p]!=0){
				ll yuxuan_i=pos[p];//预选i的位置
				ll yuxuan_j=i;
//这个条件实现了对候选对 (candidate_i, candidate_j) 和当前最佳对 (best_i, best_j) 的字典序比较,保证最终选择 i 最小,如果 i 相同,再选 j 最小的那组
				if(yuxuan_i<best_i||(yuxuan_i==best_i&&yuxuan_j<best_j)){
					best_i=yuxuan_i;
					best_j=yuxuan_j;
				}
			}else{
				pos[p]=i;
			}
			
		}
	}
	if(best_i==n+1){
		cout<<"-1 -1\n";
	}else{
		cout<<best_i<<" "<<best_j<<'\n';
	}
	
	return 0;
}
#include <bits/stdc++.h>
#define rep(a,b,c) for (int a = (b) ; a < (c) ; ++ a)
using namespace std ;
using ll = long long ;
using pii = pair<int,int> ;
const int maxn = 1e6 + 10 ;
int prime[maxn] ;
bitset<maxn> vis ; 
vector<int> vec[maxn] ; 
int a[maxn] ;
int pos[maxn] ;
int main(){
    ios::sync_with_stdio(false) ;
    cin.tie(0);
    cout.tie(0) ;
    int n ;
    cin >> n ;
    vis[1] = 1 ;
    rep(i,2,maxn){
        if (!vis[i]) prime[++ prime[0]] = i;
        for (int j = 1 ; j <= prime[0] && prime[j] * i < maxn ; ++ j){
            vis[prime[j] * i] = 1;
            if (i % prime[j] == 0) break ;
        }
    }
    for (int i = 1 ; i <= prime[0] ; ++ i)
        for (int j = 1 ; j * prime[i] < maxn ; ++ j)
            vec[prime[i] * j].push_back(prime[i]) ;
    int t ;
    vector<pii> res ;
    rep(i,1,n + 1){
        cin >> a[i] ;
        int last = n + 1;
        for (auto &t : vec[a[i]]){
            if (pos[t]) 
                last = min(pos[t],last) ;
            else 
                pos[t] = i ;
        }
        res.push_back({last,i}) ;
    }
    sort(res.begin(),res.end(),[](const pii &a,const pii &b){
        return a.first == b.first ? a.second < b.second : a.first < b.first ;
    }) ;
    cout << res[0].first << ' ' << res[0].second << endl ;
    return 0 ;
}

 J.子树的大小

题目不难,理清计算思路很好写。

图上在找falg时,应该是nodes[i]<=k&&nodes[i+1]>k,写错了。 

调了两个小时了要,先放放不想调了 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e9+1;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;
	while(t--){
		ll n,m,k;cin>>n>>m>>k;
		//二叉树一个端点
		vector<ll> nodes(61,1);//  2^60>1e18
		//存每层的节点数
		ll ans=1;
		for(ll i=0;i<=61;i++){
			if(nodes[i]>=N)break;
			nodes[i]+=ans;//到底记录每层的结点数更方便,还是前缀和,要和下文统一
			ans*=m;
		}
		//for(ll i=0;i<nodes.size();i++)cout<<nodes[i]<<" ";
		//flag记录要找的节点K位于第几层
		ll flag=0;
		for(ll i=0;i<=60;i++){
			if(k<nodes[i]){
				flag=(i-1)+1;//位于i的前一层,下标从1开始所以要加1
				break;
			}
		}
		//cout<<flag<<'\n';
		//求树高
		ll shugao=1;
		for(ll i=0;i<=60;i++){
			if(n<nodes[i]){
				shugao=i;
				break;
			}
		}
		//cout<<shugao<<'\n';
		/*
		  n_th:最高的那层的总结点数
		  jiange:把k作为根节点,k对应的满子树的最高层应该有的节点数。
		  
		 */
		ll n_th=0;
		ll sum=0;
		for(ll i=0;i<shugao-1;i++)sum+=nodes[i];
		n_th=n-sum;
	    //cout<<n_th;
		ll jiange=(ll)pow(m,(shugao-flag));
		//cout<<jiange;
		ll x=(n_th%jiange==0)? (n_th/jiange):(n_th/jiange+1);
		//cout<<x;
		
		ll k_th=0;
		ll st=1;
		for(ll i=1;i<=flag-1;i++){
			st+=(ll)pow(m,(i-1));
		}
		k_th=(k-st)+1;
		//cout<<st<<" "<<k_th;
		
		ll sum1=0;
		if(k_th<x){
			sum1=(pow(m,shugao-flag+1)-1)/(m-1);
		}else{
			sum1=(pow(m,shugao-flag)-1)/(m-1)+(n_th-jiange*(x-1));
		}
		
		cout<<sum1<<'\n';
	}
	return 0;
}

答案代码: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;
	while(t--){
		ll n,m,k;cin>>n>>m>>k;
		ll res=0;//记录最终结果
		ll ans=1;//每层为满时的层节点数
		ll l=k,r=k;
		while(1){
			res+=ans;
			if(r*m+1>n){//第n个节点在最后一层最右边节点的左边
				if((l-1)*m+2<=n){//最左边节点,第n个节点,最右边节点,在同层
					res+=(n-((l-1)*m+2)+1);//坐标相减+1
				}
				break;
			}
			l=(l-1)*m+2;//更新
			r=r*m+1;
			ans*=m;
		}
		cout<<res<<'\n';
	}
	return 0;
}

#include <iostream>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    while(T--){
        long long n, m, k;
        cin >> n >> m >> k;
        long long ans = 0;
        // 初始化左右下标和本层子树的节点数
        long long l = k, r = k, res = 1;
        while (true) {
            ans += res;  // 累加本层的节点数
            // 判断下一层是否可以完全构成
            if (r * m + 1 > n) {
                // 下一层不满,计算剩余部分能有多少节点
                if ((l - 1) * m + 2 <= n) {
                    ans += (n - ((l - 1) * m + 2) + 1);
                }
                break;
            }
            // 下一层是满的,则继续往下
            res *= m;
            r = r * m + 1;
            l = (l - 1) * m + 2;
        }
        cout << ans << "\n";
    }
    return 0;
}

end.........

### 蓝桥杯 C语言 DFS 和 BFS 真题及解答 #### 关于蓝桥杯中的DFS和BFS 蓝桥杯作为一项面向全国大学生的编程竞,其试题涵盖了多种基础算法的应用场景。其中,深度优先搜索(DFS)和广度优先搜索(BFS)是最常见的两种图遍历算法,在比中经常被用来解决路径查找、连通性分析等问题[^1]。 以下是几道典型的蓝桥杯C语言真题及其对应的解法: --- #### 题目一:迷宫最短路径问题 **描述**: 给定一个二维网格表示的迷宫地图,起点为(0, 0),终点为目标坐标(x, y)。求从起点到目标坐标的最少步数。假设每次可以向上下左右四个方向移动一步。 **解法**: 此问题可以通过BFS来实现。利用队列存储当前节点以及到达该节点所需的步数,逐层扩展直到找到目标位置为止。 ```cpp #include <queue> using namespace std; struct Node { int x, y; int step; // 当前步数 }; bool isValid(int x, int y, vector<vector<int>>& maze) { return (x >= 0 && x < maze.size() && y >= 0 && y < maze[0].size() && maze[x][y] == 0); } int bfs(vector<vector<int>> &maze, pair<int, int> start, pair<int, int> end) { queue<Node> q; q.push({start.first, start.second, 0}); const int dx[] = {0, 0, -1, 1}; const int dy[] = {-1, 1, 0, 0}; // 上下左右 while (!q.empty()) { auto current = q.front(); q.pop(); if (current.x == end.first && current.y == end.second) { return current.step; } for (int i = 0; i < 4; ++i) { int newX = current.x + dx[i]; int newY = current.y + dy[i]; if (isValid(newX, newY, maze)) { maze[newX][newY] = 1; // 标记已访问 q.push({newX, newY, current.step + 1}); } } } return -1; // 如果无法抵达目的地返回-1 } ``` 上述代码实现了基于BFS的迷宫最短路径计算方法[^2]。 --- #### 题目二:岛屿数量统计 **描述**: 在给定的一个由'0'(水)'1'(陆地)成的二维数中,找出有多少个独立的岛屿。“岛”是指通过水平或垂直连接的一片连续‘1’区域。 **解法**: 采用DFS递归方式标记已经访问过的陆地块,并记录每一块新发现的土地所属的不同岛屿编号。 ```cpp void dfsMarkIsland(vector<vector<char>>& grid, int row, int col){ if(row<0 || col<0 || row>=grid.size()||col>=grid[row].size()){ return ; } if(grid[row][col]=='0')return ; grid[row][col]='0';// 将当前位置置零代表已被处理过 static const int dir_x[]={-1,+1,0 ,0 }; static const int dir_y[]={0 ,0 ,-1,+1 }; for(auto d=0;d<4;++d){ dfsMarkIsland(grid,row+dir_x[d],col+dir_y[d]); } } int countIslands(vector<vector<char>>& grid){ int islandCount=0; for(size_t r=0;r!=grid.size();++r){ for(size_t c=0;c!=grid[r].size();++c){ if('1'==grid[r][c]){ ++islandCount; dfsMarkIsland(grid,r,c); } } } return islandCount; } ``` 这段代码展示了如何运用DFS完成对矩阵内所有孤立子集的有效计数操作[^3]。 --- #### 总结 以上两道题目分别体现了BFS适用于寻找最优解的特点,而DFS则擅长探索所有的可能性并进行分讨论的能力。对于参加蓝桥杯的同学来说,熟练掌握这两种基本算法不仅有助于提高解题效率,还能增强逻辑思维能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值