2023牛客寒假算法基础集训营4

A题

题意:总共20张牌,求x进制还是y进制表示的数字多。

思路:用20 除以进制数表示每个数字有几张牌,也就是数字最多有几位,每位可以填进制内的数字,所以表示的数字就是进制的几位的乘方,但是要把3进行特判。这个题有个结论,3进制表示的数字最多,2,4进制表示的数字一样,剩下的进制越大表示的数字越少。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ll n,m;
    cin>>n>>m;
    ll tn=20/n,tm=20/m;
    if(n==3||m==3)cout<<3<<endl;
    else if(pow(n,tn)<pow(m,tm))cout<<m;
    else if(pow(n,tn)>pow(m,tm))cout<<n;
    else cout<<min(n,m);
    return 0;
}

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef double D;
int a[1010][1010],s[1010][1010];
int main() {
    IOS;
    CC;
    int n,m;
    cin>>n>>m;
    if(n==3||m==3)cout<<3<<endl;
    else cout<<min(n,m)<<endl;
    return 0;
}

C题

思路:就是一个01背包的问题,再枚举一边n个蝴蝶结,在判断一下就可以了。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 110, M = 1e9 + 7;
int v[N],w[N],dp[N];
int main() {
    IOS;
    CC;
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> w[i] >> v[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j) continue;
            for (int k = m; k >= w[j]; k--) {
                dp[k] = max(dp[k], dp[k - w[j]] + v[j]);
            }
        }
        ll ans = dp[m] - dp[m - w[i]] - v[i] + 1;
        if (ans < 0) cout << 0 << endl;
        else cout << ans << endl;
    }
    return 0;
}

E题

思路:一个一个打死怪兽是最好的,然后就考虑每个怪兽打多少下即可,有个比较好写的写法就是我们先砍一刀,然后剩下的就是没砍死和恢复的过程,求数量的时候上取整即可。

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

int main() {
    long long int n, t, a, rr, rrr, res = 0, flag = 0, h, v;
    cin >> n >> t >> a;
    for (int i = 0; i < n; i++) {
        cin >> h >> v;
        res += t, h -= a;
        if (h <= 0) {
            continue;
        }
        if (t * v >= a) {
            cout << "-1";
            return 0;
        } else {
            res += (h / (a - t * v)) * t;
            if (h % (a - t * v)) {
                res += t;
            }
        }
    }
    cout << res - t + 1;
}

L题

题意:给三个点的权值Va,Vb,Vc,Va=lb+lc,Vb=la+lc,Vc=lb+la,求出la,lb,lc,判断能否构成三角形,能就输出Yes,并输出三条边,否则输出No。

思路:解三元一次方程求出边长,但求解的公式中有()/2所以()里面的数必须为偶数,不然边就不是正整数了,所以再判断三角形之前先判断奇数还是偶数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll va, vb, vc;
ll lb, la, lc;
ll a, b, c;
int main() {
    cin >> t;
    while (t--) {
        scanf("%lld%lld%lld", &va, &vb, &vc);
        b = (vc + va - vb);
        a = (vc + vb - va);
        c = (va + vb - vc);
        bool flag = true;
        if (a % 2 != 0 || b % 2 != 0 || c % 2 != 0)flag = false;
        lb = b / 2;
        la = a / 2;
        lc = c / 2;
        if (flag && la + lb > lc && la + lc > lb && lb + lc > la && abs(la - lb) < lc && abs(lb - lc) < la &&
            abs(la - lc) < lb)
            printf("Yes\n%lld %lld %lld\n", la, lb, lc);
        else printf("No\n");
    }
    return 0;
}

M题

题意:求n个数,这n个数中,每三个相邻的数不能构成三角形。最后输出这十个数。

思路:先初始定义三个数不能构成三角形,然后从第四个数开始循环,每次从一开始循环,只要满足条件,就break,将这个数push进数组就可,当满足n个数结束循环。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int main() {
    int n;
    cin >> n;
    vector<int> ve = {1, 1, 2};
    while(ve.size()<n) {
        int k = ve.size() - 1;
        for(int i=1;;i++)
        if (ve[k] + ve[k - 1] <= i || ve[k] + i <= ve[k - 1] || i + ve[k - 1] <= ve[k]){
            ve.push_back(i);
            break;
        }
    }
    for (auto i: ve)cout << i << ' ';
    return 0;
}

2023牛客寒假算法基础集训营6

A题

签到题,按同意模拟即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N=100010;
int a[N],b[N],c[N];
int as[N],cs[N];
int cnt[N],s[N];
int main() {
    IOS;
    CC;
    ll n;
    cin>>n;
    if(n>=1&&n<=7)cout<<"very easy";
    if(n>7&&n<=233)cout<<"easy";
    if(n>233&&n<=10032)cout<<"medium";
    if(n>10032&&n<=114514)cout<<"hard";
    if(n>114514&&n<=1919810)cout<<"very hard";
    if(n>1919810)cout<<"can not imagine";
    return 0;
}

C题

题意:给n个数,不断融合这n个数,就是每一轮相邻的两个数相加形成一个新的数,直至加到只剩一个数,就是最后的值,要是这个值尽可能大,输出这n个数的排列方法。

思路;要使值尽可能大,那么就要将较大的数放再中间,多加几次,所以先从中间开始,从大到小依次排列,最后再用两个for循环求出最后的值就是最大的。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 1010, M = 1e9 + 7;
int a[N], b[N], c[N];
int as[N], cs[N];
int cnt[N], s[N];
int main() {
    IOS;
    CC;
    int n;
    cin >> n;
    int t = n, k = 0;
    if (n & 1) {
        a[n / 2 + 1] = t--;
        for (int i = n / 2, j = n / 2 + 2; i > 0; i--, j++) {
            if (k % 2 == 0)a[i] = t--, a[j] = t--, k++;
            else a[j] = t--, a[i] = t--, k++;
        }
    } else {
        for (int i = n / 2, j = n / 2 + 1; i > 0; i--, j++) {
            if (k % 2 == 0)a[i] = t--, a[j] = t--, k++;
            else a[j] = t--, a[i] = t--, k++;
        }
    }
    for (int i = 1; i <= n; i++)b[i] = a[i];
    for (int i = n; i >= 1; i--) {
        for (int j = 1; j <= i - 1; j++) {
            b[j] = b[j] % M + b[j + 1] % M;
        }
    }
    cout << b[1] % M << endl;
    for (int i = 1; i <= n; i++)cout << a[i] << ' ';
    return 0;
}

F题

题意:阿宁有一个长度为 � n的数组 � a,下标从 1 1开始。

定义 � ( � ) f(x)是x在二进制表示下1的个数。例如5的二进制是101因此f(5)=2阿宁有q次询问,每次询问后数组恢复原样。 在一次询问中,给出k。在一次操作中,可以选择一个数(1≤i≤n),将a[i]修改为f(a[i])请你恰好进行k次操作,要求整个数组的最大值最小,这个最大值是多少?

思路:这个题可以使用堆来做,优先队列,用大根堆,维护最大的值到堆顶,再结合位运算求二进制里1的个数,最后输出即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 110, M = 1e9 + 7;
int v[N],w[N],dp[N];
priority_queue<int> Q;
vector<int> ans;
int f(int num){
    int res=0;
    while(num){
        if(num&1) res++;
        num>>=1;
    }
    return res;
}
int main() {
    IOS, CC;
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        int temp;
        cin >> temp;
        Q.push(temp);
    }
    while (Q.top() != 1) {
        int temp = Q.top();
        Q.pop();
        temp = f(temp);
        Q.push(temp);
        ans.push_back(Q.top());
    }
    for (int i = 1; i <= q; i++) {
        int k;
        cin >> k;
        if (k > ans.size()) cout << "1" << endl;
        else cout << ans[k - 1] << endl;
    }
    return 0;
}

H题

题意:先杀死y个脆皮,当杀了这y个脆皮以后还剩脆皮就可以攻击到,不剩下就不能攻击到,求能被攻击到的概率,要进却到1e-6以上才可以。

思路:此题分三种情况,一定不能被攻击到,一定能被攻击到,可能被攻击到,分别讨论即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N=100010;
int a[N],b[N],c[N];
int as[N],cs[N];
int cnt[N],s[N];
int main() {
    IOS;
    CC;
    double x,l,r;
    cin>>x>>l>>r;
    if(x<l)cout<<0;
    else if(x>r)cout<<1;
    else printf("%.16f",(x-l)/(r-l+1));
    return 0;
}

G题

题意:阿宁有一个长度为n的整数数组a,阿宁想要在其中选出恰好k对整数,使得每对整数相乘并求和尽可能大。阿宁想知道最终得到的值是多少?

思路:要使负负相乘,整整相乘,所以先排个序,使用双指针,一个从头开始,一个从尾部,每次前两个数和后两个数比较相乘的大小,大的加到总和里并且移动这两个数对应的指针,循环结束即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N=200010;
int a[N],b[N],c[N];
int as[N],cs[N];
int cnt[N],s[N];
int main() {
    IOS;
    CC;
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; i++)cin >> a[i];
    sort(a, a + n);
    int res = 0, ans = 0;
    int i = 0, j = n - 1;
    while (1) {
            if (a[i] * a[i + 1] <= a[j] * a[j - 1])
                res += a[j] * a[j - 1], j -= 2, ans++;
            else res += a[i] * a[i + 1], i += 2, ans++;
        if (ans == k)break;
    }
    cout << res << endl;
    return 0;
}

SMU 2023 年冬季赛 #9 (Div.2)

A题

签到题

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N=1e5+10;
int a[N],b[N],c[N];
int n,m;
int main() {
    IOS;
    CC;
    cout<<"Ranni the Witch";
    return 0;
}

B题

题意:有n个怪,前k秒每秒可以群伤x,之后群伤y,求干掉所有怪需要的最少时间

思路:先找出血最厚的怪,然后分类讨论,<kx直接做除法,不够的话-kx再除y,注意取整即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 110, M = 1e9 + 7;
int v[N],w[N],dp[N];
int main() {
    IOS, CC;
    int T;
    cin >> T;
    while (T--) {
        int n, k, x, y;
        cin >> n >> k >> x >> y;
        int mx;
        cin >> mx;
        for (int i = 2; i <= n; i++) {
            int t;
            cin >> t;
            mx = max(mx, t);
        }
        if (k * x >= mx) {
            int t = mx / x;
            if (mx % x != 0)t++;
            cout << t << "\n";
        } else {
            mx -= k * x;
            int t = k + mx / y;
            if (mx % y != 0)t++;
            cout << t << "\n";
        }
    }
    return 0;
}

C题

题意:给出两段冒泡排序的伪代码,求构造一个长为n的排列(不重),满足使用两段代码排序的交换次数相同。

思路:对于冒泡排序,其交换次数= 逆序对数量,可以发现若构造的排列a1 = n,两个算法的交换次数都是其逆序对数量。所以随便输出一个首位为n 的排列即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 110, M = 1e9 + 7;
int v[N],w[N],dp[N];
int main() {
    IOS, CC;
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = n; i >= 1; i--) {
            cout << i << " ";
        }
        cout << "\n";
    }
    return 0;
}

F题

题意:T组(1e5)数据,每次给出n, k(1e9),求1~n所有数除以pow(2,k)后分母部分相加的结果。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
typedef double D;
const int N = 110, M = 1e9 + 7;
int v[N],w[N],dp[N];
int main() {
    IOS, CC;
    int T;
    cin >> T;
    while (T--) {
        ll n, k;
        cin >> n >> k;
        ll sum = n * (n + 1) / 2;
        while (n > 0 && k > 0) {
            n >>= 1;
            sum -= n * (n + 1) / 2;
            k--;
        }
        cout << sum << "\n";
    }
    return 0;
}

L题

题意:打怪,给出怪物塔的总层数 n 和能达到的层数 k\n当打死一层的怪会获得怪的能力值,本层消失,本层上边的层数减一,求通关的最小能力值

思路:打怪的顺序是唯一的,所以维护一个有 k 个元素的小根堆,每次处理最小的元素,如果现有能力值大于怪的能力值,加上怪的能力值,否则更新初始值;\n\n每遇到一个打不过的怪,最小可能初始值 = 怪的能力值-前面获得的能力值总和,用这个值去更新之前的 初始值,取其中大的。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define IOS ios::sync_with_stdio(false)
#define CC cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
const int N = 1e6+100;
ll t,n,k;
ll a[N];
priority_queue<ll,vector<int>,greater<int> >pmin;
ll sta,now;
int main() {
    //IOS,CC;
    cin >> t;
    while (t--) {
        cin >> n >> k;
        sta = 0;
        now = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
        }
        for (int i = 1; i <= n; i++) {
            if (pmin.empty() || pmin.size() < k) pmin.push(a[i]);//注意判空
            else {
                ll s = pmin.top();
                pmin.pop();
                if (now >= s) {
                    now += s;
                }
                else {
                    sta = max(sta, s - now);
                    now += s;
                }
                pmin.push(a[i]);
            }
        }
        while (!pmin.empty()) {
            ll s = pmin.top();
            pmin.pop();
            if (now >= s) {
                now += s;
            } else {
                sta = max(sta, s - now);
                now += s;
            }
        }
        cout << sta << endl;
    }
    return 0;
}