河南萌新联赛2024第(一)场:河南农业大学

河南萌新联赛2024第(一)场:河南农业大学

2024.7.17 13:00————15:00

过题数6/12
补题数8/12

  • 造数
  • 爱探险的朵拉
  • 有大家喜欢的零食吗
  • 小蓝的二进制询问
  • 奇妙的脑回路
  • 两难抉择新编
  • 旅途的终点
  • 两难抉择
  • 除法移位
  • 最大矩阵匹配
  • 图上计数(Easy)
  • 图上计数(Hard)

A - 造数

其实题不难,但是可能脑子没转,所以没有想到逆向思维,wa了3发还很影响心态。亏我甚至还搞了个快速幂,一点关系没有。
题解:
给定一个整数n,可以进行➕1➕2✖️2这三种操作中的任意一个,问最少需要多少次操作可以将0转位n。
逆向思维从n转为0,一开始一直➗2肯定是最快的操作,不能除的时候就先➖1,然后一直到1,2,3这种特殊数字,判断剩余次数。
代码:

#include

using namespace std;
#define int long long

signed main() {
    int n;
    cin >> n;
    int ans = 0;
    while(n != 0) {
    //成功变成0
        if(n == 2 || n == 1) {
            ans += 1;
            //进行操作1或2即可
            break;
        }
        else if(n == 3 ) {
            ans += 2;
            //进行操作1和2即可
            break;
        }
        if(n % 2 != 0){
            n--;
            ans++;
            //进行操作1
        }
        n = n/2;
        ans++;
        //进行操作3
    }
    cout << ans << endl;
    return 0;
}


C - 有大家喜欢的零食吗

匈牙利算法,学了整整半天,周报里会对这个算法做解释。
题解:
有n个小朋友和n个大礼包,保证任意一个小朋友至少会喜欢一个,求最少还需要购买多少份大礼包来保证所有小朋友都吃到自己喜欢的礼包。
二分图最大匹配板子题。
代码:

#include

using namespace std;
#define int long long
int n;
int vis[510];
int a[510][510];
int lk[510];

bool dfs(int x) {
    for (int i = 1; i <= n; i++) {
        if(!vis[i] && a[x][i]){
            vis[i] = 1;
            if(!lk[i] || dfs(lk[i])) {
                lk[i] = x;
                return true;
            }
        }
    }
    return false;
}

signed main() {
    cin >> n;
    memset(a,0,sizeof a);
    memset(lk,0,sizeof lk);
    for (int i = 1; i <= n; i++) {
        int k;
        cin >> k;
        for (int j = 1; j <= k; j++) {
            int t;
            cin >> t;
            a[i][t] = 1;
        }
    }
    
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        memset(vis,0,sizeof vis);
        if(dfs(i))ans++;
    }
    if(ans == n)cout << "Yes" << endl;
    else {
        cout << "No" << endl;
        cout << n-ans << endl;
    }
    return 0;
}

D - 小蓝的二进制询问

题解:
t组询问,每组l,r,求区间中所有整数在二进制下1的个数之和,结果mod998244353。
观察且可证实,在第一位上,二进制以010101循环,在第二位上,二进制以00110011循环,以此类推的循环周期,对每一位二进制位数的1进行计算求和即可。注意最大的r,10的18次方二进制62位,所以至少要遍历62个位置的1。
代码:

#include

using namespace std;
#define int long long
const int mod = 998244353;

int po(int a,int y) {
    if(y == -1) return 0;
    int res = 1;
    while (y) {
        if(y & 1)
        res = res*a;
        y>>=1;
        a= a*a;
    }
    //int c= (res+mod)%mod;
    //return c;
    //我真的很奇怪,甚至这里res%mod也会出错,而且还是全错
    return res;
}//奇了怪了,二进制内也不可以除余,不然会出错

int f(int x,int k) {
    int y = po(2,k);
    //诶我就要用快速幂
    x++;
    int res = x/(2*y);
    res = res*y;
    int lr = x%(2*y);
    lr -= y;
    if(lr > 0)res += lr;
    return res;
}//不知道为什么内部除余会出错,纯纯不理解

signed main() {
    int t;
    cin >> t;
    
    while (t--) {
        int ans = 0;
        int l,r;
        cin >> l >> r;
        for (int i = 0; i <= 62; i++) {
            //为什么是62呢,10的18次方换算为2进制有多少位
            ans += (f(r,i)%mod-f(l-1,i)%mod+mod)%mod;
            ans = ans % mod;
            //必须在函数外除余
        }
        cout << ans << endl;
    }
    return 0;
}

F - 两难抉择新编

跟后面的俩难抉择类似,一次ac的,yeahhhhhhh
题解:
有一个长度位n的数组a,可以在数组内任意选一个数进行最多一次操作,可以使得ai = ai+x,也可以是ai=ai*x,x可以是1到n/i范围内任意正整数。
首先明确一个,a xor b = c,那么a xor c = b。就可以得到除去自己以后的数组异或和,接着对这俩种操作进行比较即可,不会超时非常棒。
代码:

#include

using namespace std;
#define int long long
int a[200005];
int yh[200005];

signed main() {
    int n;
    cin >> n;
    int s = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        s = s xor a[i];
    }
    int ma = 0;
    for (int i = 1; i <= n; i++) {
        yh[i] = s xor a[i];
        int res = yh[i];
        for (int j = 1; j <= n/i; j++) {
            res = max(res,yh[i] xor (a[i]+j));
            res = max(res,yh[i] xor (a[i]*j));
        }
        ma = max(ma,res);
    }
    cout << ma;
    return 0;
}

G - 旅途的终点

这题读题一定要准确,我理解错了很多次题意…
题解:
有n个国家,必须按照从1到n的顺序畅游这些国家,没经过一次需要耗费ai点生命力,生命力释放完毕则回国,有k次释放神力的机会,不用耗费生命力,求最多可以畅游多少个国家。
稍微讲一下我一开始的错误思路,找到最大的k个,每遇到这些国家时用神力,生命力开始不够的时候也用神力,用完为止。可以想象如果第一个数很大,但不是第k大,耗费大量生命力,后面一连串小国家则无法更多的访问。
正确思路应该是对于每插入一个,进行计算需要用几次神力,生命力不够的时候当然得用神力啦,而且必须对最大的国家用神力。然后二分一下<=k次数的神力可以访问多少个国家。
代码:

#include

using namespace std;
#define int long long
int n,m,k;
int a[200005];
int js[200005];
priority_queuec;

bool cmp(pairx,pairy){
    if(x.first == y.first)return x.second < y.second;
    else return x.first > y.first;
}

signed main() {
    cin >> n >> m >> k;
    int res = 0;
    int z = 0;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        c.push(a[i]);
        res += a[i];
        while(res >= m) {
            z++;
            res-=c.top();
            c.pop();
        }
        js[i] = z;
        
    }
    
//     for (int i = 1; i <= n; i++) {
//         cout << js[i] << endl;
//     }
    
    int l = 0,r = n;
    
    while (l < r) {
        int mid = (l+r+1)/2;
        if(js[mid] <= k)l = mid;
        else r = mid-1;
    }

    cout << l;
    return 0;
}

H - 两难抉择

题解:
题意较为简单。
找到最大的数字,然后判断对于x=n的俩种情况哪个较大即可,注意数组中有相同数字的情况。
代码:

#include

using namespace std;
#define int long long
int n;
int a[200005];

signed main() {
    cin >> n;
    int ma = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        ma = max(ma,a[i]);
    }
    int ans = 0;
    bool st = true;
    for (int i = 1;i <= n; i++) {
        if(st && a[i] == ma) {
            ans += max(a[i]*n,a[i]+n);
            st = false;
        }
        else ans += a[i];
    }
    cout << ans;
    return 0;
}

I - 除法移位

题解:
长度为n的数组a,定义s为a1除以其它所有数字,最多可以对a进行t次右移。要求s最大。
在可移动范围内,找到最大值作为a1即可,因为分子变大的同时分母变小,这个数字当然会变大。
代码:

#include

using namespace std;
#define int long long
int a[200005];

signed main() {
    int n,t;
    cin >> n >> t;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int ma = a[1];
    int wz = 0;
    int j = 0;
    if(n-t+1 > j)j = n-t+1;
    for (int i = n; i >= j; i--) {
        if(a[i] > ma) {
            ma = a[i];
            wz = n+1-i;
        }
    }
    cout << wz;
    
}

K - 图上计数(Easy)

题解:
n点m线,能删除若干条线也可以合并联通块,定义联通块大小为点的个数,代价是大小相乘。
尽量均等的分成俩个联通块即可。
代码:

#include

using namespace std;
#define int long long
int n,m;
vector>a(1000005);

signed main() {
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        cin >> a[i].first >> a[i].second;
    }
    if(n%2 == 0) {
        cout << n*n/4;
    }
    else {
        cout << n/2*(n/2+1);
    }
    return 0;
}

你可能感兴趣的:(算法,c++,数据结构,排序算法)