蓝桥 第 3 场 算法季度赛 前5题题解,剩下的后续发上去补上

第1题 全国科普行动日【算法赛】

题目传送门

直接输出就行,没多大操作可言:

#include <iostream>
using namespace std;
int main()
{
  cout<<"6.29";
  return 0;
}

第2题 A%B【算法赛】

题目传送门

题目有点小坑,因为也可能是负数,所以需要特判一下,上代码就可以看懂了:

#include <iostream>
using namespace std;

int main() {
    int T;
    cin >> T;

    while (T--) {
        long long A, B;
        cin >> A >> B;
        long long remainder = A % B;
        if (remainder < 0) {
            remainder += abs(B);
        }
        
        cout << remainder << endl;
    }

    return 0;
}

第3题 能量圆盘【算法赛】

题目传送门

一开始被题目中说的1与n是相连的给蛊惑了,后面亲自写了一个数据来验证,似乎没有什么用,单纯拿来蛊惑人心的,使用map记录一下出现次数最多的数字是什么,然后用n减去这个最大值就行了,因为每个数都必须变成同一个数,这样做毋庸置疑是最小次数:

#include<bits/stdc++.h>

using namespace std;
using ll = long long;

const ll N = 1e4 + 5;
const ll inf = 1e18;

ll a[N], dp[N];
map<ll, ll>mp;

bool cmp_value(const pair<ll, ll> left, const pair<ll, ll> right) {
	return left.second < right.second;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		mp[a[i]]++;
	}
	ll k=0,maxx=0;
	for(auto i=mp.begin();i!=mp.end();i++){
		if(i->second>maxx){
		    maxx=i->second;
		    k=i->first;
		}
	}
	ll ans=n-maxx;
//	if(a[1]==a[n]&&mp[k]==maxx&&k!=a[1]){
//		ans--;
//	}
	cout<<ans;
	return 0;
}

第4题 植物保留【算法赛】

题目传送门

这个题是一个二分题,不过群里面也听说有人用贪心来做了,但是一眼就看出是贪心,我就没想到其他方法了,直接二分答案若答案能够成立(就是说这个值的数的树能够成立)就增大l,使得答案变大继续判断,否则r变小继续判断

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

const ll N = 1e5 + 5;
const ll inf = 1e18;

ll a[N];
ll n, m;

bool check(ll mid) {
    ll segments = 1;
    ll last_position = a[1];

    for (int i = 2; i <= n; ++i) {
        if (a[i] - last_position >= m) {
            segments++;
            last_position = a[i];
        }
    }

    return segments >= mid;
}

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

    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    sort(a + 1, a + n + 1);

    ll l = 1, r = n;
    ll ans = 0;

    while (l <= r) {
        ll mid = (l + r) / 2;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }

    cout << ans << "\n";

    return 0;
}

第5题 龙骑士军团【算法赛】

题目传送门

题目大意:找到[a,b]里面的一个起点,[c,d]里面的一个终点,使得这个区间和最大。

因为在打游戏,做这题的时候没时间,粗略看了一下,是一个线段树问题,前面我也发了6篇线段树,所以能轻松看出来,不过由于各种原因调了好久(输出提示的地方忘记删了)也相当于给大家提个醒,下次一定要注意了。

思路如下:说到区间和,不得不想到前缀和,但是前缀和是sum[l,r]=a[r]-a[l-1];这样的话不难想到将前缀和数组作为线段树的“根”,维护一个最大值和最小值的线段树,然后直接在左区间找最小值,右区间找最大值,两个相减就得到了,但是,需要注意这个a[l-1],左区间的最后一个不能被取到,而左区间的前一个能被取到,这样的话就难办了,而我思考后想到——可以将线段树整体右移动一格,然后呢就空出第一个位置来了,查询的时候就用

ll str=query(1,1,n+1,l-1,r-1);

 ll end=queryy(1,1,n+1,l,r);

但是需要明白,l和r需要自加一下,因为整体右移动了,接下来直接看代码就行。

这个线段树比较好写,相较于之前的几棵树,这个没有laze和down函数,也没有数据更新函数,比较轻松的: 

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll N = 2e5 + 5;
const ll inf = 1e18;

ll tree_max[N<<2],tree_min[N<<2],a[N];

ll lson(ll i){
	return i<<1;
}
ll rson(ll i){
	return i<<1|1;
}
void up(ll i){
	tree_max[i]=max(tree_max[lson(i)],tree_max[rson(i)]);
	tree_min[i]=min(tree_min[lson(i)],tree_min[rson(i)]);
}
void build(ll i,ll pl,ll pr){
	if(pl==pr){
		tree_max[i]=tree_min[i]=a[pl];
		return;
	}
	ll mid=(pl+pr)>>1;
	build(lson(i),pl,mid);
	build(rson(i),mid+1,pr);
	up(i);
}

ll query(ll i,ll pl,ll pr,ll L,ll R){
	if(L<=pl&&pr<=R){
		return tree_min[i];
	}
	ll mid=(pl+pr)>>1;
	ll minn=inf;
	if(L<=mid){
		minn=min(minn,query(lson(i),pl,mid,L,R));
	}
	if(R>mid){
		minn=min(minn,query(rson(i),mid+1,pr,L,R));
	}
	return minn;
}

ll queryy(ll i,ll pl,ll pr,ll L,ll R){
	if(L<=pl&&pr<=R){
		return tree_max[i];
	}
	ll mid=(pl+pr)>>1;
	ll maxx=-inf;
	if(L<=mid){
		maxx=max(maxx,queryy(lson(i),pl,mid,L,R));
	}
	if(R>mid){
		maxx=max(maxx,queryy(rson(i),mid+1,pr,L,R));
	}
	return maxx;
}

int main() {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n,q;
    cin>>n>>q;
    for(int i=2;i<=n+1;i++){
      cin>>a[i];
      a[i]+=a[i-1];
    }
    build(1,1,n+1);
    while(q--){
      ll l,r;
      cin>>l>>r;
      l++,r++;
      ll str=query(1,1,n+1,l-1,r-1);
      cin>>l>>r;
      l++,r++;
      ll end=queryy(1,1,n+1,l,r);
      cout<<end-str<<'\n';
    }
    return 0;
}

因为我每周六都要去打游戏,所以算法赛没多少时间写,这次算法赛感觉还行,好久没写这种题了,连二分的那个题都写了6次才过,废了,后面的题没时间看,感觉应该也还行,能写,今天先发这几个题,后面的3个题我会陆续发,可以收藏期待一下吖,那么今天学习到此结束,拜拜吖!

第6题 热身操领队【算法赛】

题目传送门

这题我能看出来是线段树,但是不太会做,尝试无果后请教了学长,然后学长轻轻松松解出来了,不得不说学长强的可怕,看了学长思路后,我也尝试重新写了一下,也理解了是什么意思,不过下次再出我也不一定做得出来,应该是叫做离散化吧,没听说过,这题语言不好讲,直接看代码好一点:

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
const ll N = 2e5 + 5;

ll tree[N << 2], rk[N], a[N], v[N], n;

ll lson(ll i) {
	return i << 1;
}
ll rson(ll i) {
	return i << 1 | 1;
}

void up(ll i) {
	tree[i] = tree[lson(i)] + tree[rson(i)];
}

void updata(ll i, ll pl, ll pr, ll L, ll R, ll a) {
	if (pl == pr) {//给每次进来的人加上去,进来多少人就加多少
	
		tree[i] += a;
		return ;
	}
	ll mid = (pl + pr) >> 1;
	if (L <= mid) {//单点更新
		updata(lson(i), pl, mid, L, R, a);
	} else {
		updata(rson(i), mid + 1, pr, L, R, a);
	}
	up(i);
}

ll query(ll i, ll pl, ll pr, ll L, ll R) {//线段树板子,用来查询区间和
	if (L <= pl && pr <= R) {
		return tree[i];
	}
	ll mid = (pl + pr) >> 1;
	ll ans = 0;
	if (L <= mid) {
		ans += query(lson(i), pl, mid, L, R);
	}
	if (R > mid) {
		ans += query(rson(i), mid + 1, pr, L, R);
	}
	return ans;
}

ll chek(ll num) {//二分查找中位数
// 如果左边区间和大于中位数,说明中位数在左边,反之在右边
	int l = 1, r = n;
	while (l <= r) {
		int mid = l + r >> 1;
		if (query(1, 1, n, 1, mid) >= num) {
			r = mid - 1;
		} else l = mid + 1;
	}
	return l;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> v[i] >> a[i];
		rk[i] = v[i];
	}
	sort(rk + 1, rk + 1 + n);
	int cont = 0;
	for (int i = 1; i <= n; i++) {
		if (rk[i] != rk[i - 1]) {
			rk[++cont] = rk[i];
		}//将身高的类型统计出来,即身高的“排名”
	}
	for (int i = 1; i <= n; i++) {
		//pos查找该更新什么地方
		int pos = lower_bound(rk + 1, rk + 1 + cont, v[i]) - rk;
		updata(1, 1, n, pos, pos, a[i]);//在该身高位置处更新数据(累加)
		if (tree[1] % 2) {
			cout << rk[chek(tree[1] / 2 + 1)] << '\n';
		} else {
			cout << rk[min(chek(tree[1] / 2), chek(tree[1] / 2 + 1))] << '\n';
		}
	}
	return 0;
}

很累啊,难想出来的,看来我实力还不够,差得远哭哭哭。 

第7题 单词博弈【算法赛】

题目传送门​​​​​​​

这个题就是一个大模拟题,我写了好久,一直只能通过80%,写的我裂开了,最后借鉴了一位大佬的代码,感觉都是纯模拟,不知道为什么我的就过不了,这是我的代码(没AC版):

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll N = 1e5 + 5;

struct cmp {
    bool operator()(const string& s1, const string& s2) const {
        // 自定义的比较逻辑
        return s1 > s2;
    }
};

priority_queue<string, vector<string>, cmp>Lan, Qiao;

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    ll n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        string a;
        cin >> a;
        Lan.push(a);
    }
    for (int i = 1; i <= m; i++) {
        string a;
        cin >> a;
        Qiao.push(a);
    }
    string now = Lan.top();
    Lan.pop();
    ll i = 0;
    while (1) {
        i++;
        if (i % 2) {
            if(Qiao.empty()){
                cout<<"L";
                return 0;
            }
            while (!Qiao.empty()) {
                string temp = Qiao.top();
                Qiao.pop();
                m--;
                if (temp > now) {
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        now = temp;
                        break;
                    } else {
                        cout << "L";
                        return 0;
                    }
                } else {
                    while (!Qiao.empty() && temp <= now) {
                        temp = Qiao.top();
                        Qiao.pop();
                        m--;
                    }
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        now = temp;
                        break;
                    } else {
                        cout << "L";
                        return 0;
                    }
                }
            }
        } else {
            if(Lan.empty()){
                cout<<"Q";
                return 0;
            }
            while (!Lan.empty()) {
                string temp = Lan.top();
                Lan.pop();
                n--;
                if (temp > now) {
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        now = temp;
                        break;
                    } else {
                        cout << "Q";
                        return 0;
                    }
                } else {
                    while (!Lan.empty() && temp <= now) {
                        temp = Lan.top();
                        Lan.pop();
                        n--;
                    }
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        now = temp;
                        break;
                    } else {
                        cout << "Q";
                        return 0;
                    }
                }
            }
        }
        if (n == 0) {
            if (!Qiao.empty()) {
                string temp = Qiao.top();
                Qiao.pop();
                m--;
                if (temp > now) {
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        cout << "Q";
                        return 0;
                    }
                } else {
                    while (!Qiao.empty() && temp <= now) {
                        temp = Qiao.top();
                        Qiao.pop();
                        m--;
                    }
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        cout << "Q";
                        return 0;
                    } else {
                        cout << "L";
                        return 0;
                    }
                }
            } else {
                cout << "L";
                return 0;
            }
        }

        //
        if (m == 0) {
            if (!Lan.empty()) {
                string temp = Lan.top();
                Lan.pop();
                n--;
                if (temp > now) {
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        cout << "L";
                        return 0;
                    }
                } else {
                    while (!Lan.empty() && temp <= now) {
                        temp = Lan.top();
                        Lan.pop();
                        n--;
                    }
                    if (temp[0] == now[0] || temp[0] == now[0] + 1) {
                        cout << "L";
                        return 0;
                    } else {
                        cout << "Q";
                        return 0;
                    }
                }
            } else {
                cout << "Q";
                return 0;
            }
        }
    }
    return 0;
}

可以看到我写了160多行代码,好多地方都判断了,就是过不了,难受死了,如果有知道我错什么地方的,欢迎直接评论告诉我。

接下来是大佬的AC代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

string Lan[N], Qiao[N];

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> Lan[i];
    }
    
    for (int i = 0; i < m; i++) {
        cin >> Qiao[i];
    }
    
    int x1 = 1, x2 = 0;

    while (x1 <= n && x2 < m) {
        if ((Qiao[x2] > Lan[x1 - 1] && Qiao[x2][0] == Lan[x1 - 1][0]) ||
            (Qiao[x2] > Lan[x1 - 1] && Qiao[x2][0] == (char)(Lan[x1 - 1][0] + 1))) {
            x1++;
        } else {
            x2++;
            continue;
        }
        
        if ((Lan[x1 - 1] > Qiao[x2] && Lan[x1 - 1][0] == Qiao[x2][0]) ||
            (Lan[x1 - 1] > Qiao[x2] && Lan[x1 - 1][0] == (char)(Qiao[x2][0] + 1))) {
            x2++;
        } else {
            while (x1 <= n) {
                if ((Lan[x1 - 1] > Qiao[x2] && Lan[x1 - 1][0] == Qiao[x2][0]) ||
                    (Lan[x1 - 1] > Qiao[x2] && Lan[x1 - 1][0] == (char)(Qiao[x2][0] + 1))) {
                    x2++;
                    break;
                } else {
                    x1++;
                }
            }
        }
    }

    if (x1 == n + 1) {
        cout << "Q" << endl;
    } else if (x2 == m) {
        cout << "L" << endl;
    }
    
    return 0;
}

这个我感觉也是模拟,不知道为什么就可以过了555。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值