第三次周赛

admin 发布于 2025-11-10 1687 字 120 次阅读


A:初见组合数学

球在分叉点有两种情况,向左和向右;

由此可算出最底一层每个格子的情况,所有格子加起来为总情况;

每个格子的情况数除总情况即为答案;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"

void solve(){
    int n;
    cin >> n;
    vector<vector<int>> c(n + 1,vector<int>(n + 1));
    for (int i = 0; i <= n; i++){
        for (int j = 0; j <= i; j++){
            if (!j) c[i][j] = 1;
            else c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
        }
    }
    int ans = 0;
    for (int i = 0; i <= n; i++){
        ans += c[n][i];
    }
    for (int i = 0; i <= n; i++){
        double t =  c[n][i] / 1.0 / ans;
        cout << fixed << setprecision(3) << t << " ";
    }
    cout << endl;
    
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}

B:最大子段和

从头向后遍历,将该处的值加入答案;

如果答案小于0,则这一段的贡献为负,直接抛弃即可,将答案重置为0;

遍历完的答案为最终结果;

想不通找学长();

#include<iostream>
using namespace std;
#define int long long
const int N = 2010; 
int n;
int a[N];

signed main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
    }
    int ans = 0, res = 0;
    for (int i = 1; i <= n; i++){
        ans += a[i];
        res = max(res,ans);
        if (ans < 0) ans = 0;
    }
    cout << res << endl;
}

C:回文数组

顾名思义,头尾对应相等的数组是回文数组;

第一个对应最后一个,第二个对应倒数第二个........;

如果发现有不对应的,就不是;

全部检查完了没有问题就是回文数组;

#include<iostream>
using namespace std;
#define int long long
const int N = 1005;
int a[N];
int n;

signed main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
    }
    int f = 1;
    for (int l = 1, r = n; l <= r; l++, r--){
        if (a[l] != a[r]){
            f = 0;
            break;
        }
    }
    if (f) cout << "YES" << endl;
    else cout << "NO" << endl;
}

D:变化的数

这两个操作都是让n变小的操作,如果操作到n小于m时,就可以不用再操作了;

可以发现/2和开根号都会让n迅速减小,所以完全可以将所有操作都模拟一遍;

比如先除2,再全开根号或先开根号再全除2等等操作;

此处需要用到dfs(深度优先搜索),没有了解的可以去查,自己学习一下;

将所有情况遍历完如果能等于m就输出yes,反之no;

#include<bits/stdc++.h>
using namespace std;
int n, m;

bool dfs(int x){
    if (x < m) return false;
    if (x == m) return true;
    int res = 0;
    res |= dfs(x / 2);
    res |= dfs(sqrt(x));
    return res;
}

signed main(){
    cin >> n >> m;
    int f = dfs(n);
    if (f) cout << "YES" << endl;
    else cout << "NO" << endl;
}

E:数矩形

可以先思考1*n的方格块以及1*m的方格块分别可以组成多少个矩形;

两者相乘就是答案;

想不明白可在群里问;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"

void solve() {
    int n, m;
    cin >> n >> m;
    cout << n * (n + 1) / 2 * m * (m + 1) / 2 << endl;
}


signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}

F:刘学姐的差分数组

正常写的人一定会超时;可以自己算一下时间复杂度;

本题可以说是差分板子题,一定要补();

举个例子,有个数组全是零,现在我将l和r+1处分别变成1和-1;

求其前缀和(应该没有人不会了吧,不会快去补!),是不是可以发现只有l到r的前缀和是1;

其他部分前缀和都是0,这样是不是就把l到r标记了,而且时间复杂度为O(1);

本题中前缀和cnt小于等于0时就代表没有被标记覆盖;

不明白的可以去查一下,很重要;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"

void solve() {
    int n,ans=0;
    cin >> n;
    vector<int> a(n + 2);
    int m; cin >> m;
    for (int i = 1; i <= m; i++){
        int l, r;
        cin >> l >> r;
        a[l] += 1, a[r + 1] -= 1;
    }    
    int cnt = 0;//前缀和
    for (int i = 0; i <= n; i++){
        cnt += a[i];
        if(cnt<=0) ans++;
    }
    cout << ans << endl;
}


signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}

G:逆序对

二进制数组由01构成

可以想到1在0前面就是一个逆序对;

所以思路是先求出现有的逆序对个数;

然后对于每个位置都反转一下看谁的贡献最高;

最高贡献加现有个数就是答案;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
int n;

void solve() {
    cin >> n;
    vector<int> a(n + 2), s1(n + 2), s2(n + 2);
    for (int i = 1; i <= n; i++){
        cin >> a[i];
    }
    int sum = 0, cnt = 0;
    for (int i = n; i >= 1; i--){
        if (a[i] == 0) cnt++;
        else sum += cnt;
    }
    int ans = sum;
    for (int i = 1; i <= n; i++){
        s1[i] = s1[i - 1] + (a[i] == 1);
    }
    for (int i = n; i >= 1; i--){
        s2[i] = s2[i + 1] + (a[i] == 0);
    }
    for (int i = 1; i <= n; i++){
        if (a[i] == 1) ans = max(ans, sum + s1[i - 1] - s2[i + 1]);
        if (a[i] == 0) ans = max(ans, sum + s2[i + 1] - s1[i - 1]);
    }
    cout << ans << endl;
}


signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}

H:离散化

数据有点水,本来没人可以出的;

离散化是很重要的算法基础;

比如在1到1e18的范围,却只有很少的数,如果遍历一定会超时,这时候就要用离散化;

用处是将数集中起来,原本一个数和另一个数可能中间相隔很长,离散化可以让它们变得挨在一起;

这样就不会超时;

上网搜离散化板子,自行学习;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
typedef pair<int, int> PII;  
const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;

int find(int x){
    int l = 0, r = alls.size() - 1;
    while (l < r){
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}

signed main(){
    cin >> n >> m;
    for (int i = 0; i < n; i++){
        int x, c;
        cin >> x >> c;
        add.push_back({ x, c });
        alls.push_back(x);
    }
    for (int i = 0; i < m; i++){
        int l, r;
        cin >> l >> r;
        query.push_back({ l, r });
        alls.push_back(l);
        alls.push_back(r);
    }
    //去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()),alls.end());

    for (auto item : add){
        int x = find(item.first);
        a[x] += item.second;
    }

    for (int i = 1; i <= alls.size(); i++) s[i] = s[i - 1] + a[i];
    for (auto item : query){
        int l = find(item.first) , r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }
}
最后更新于 2025-11-10