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

文章目录

  • A
  • B
  • C
  • D
  • F
  • G
  • H
  • I

A

思路:就语法题

#include 
using namespace std;

int main()
{
    long long x;
    cin >> x;
    if (x >= 1 && x <= 7)
        cout << "very easy";
    else if (x <= 233)
        cout << "easy";
    else if (x <= 10032)
        cout << "medium";
    else if (x <= 114514)
        cout << "hard";
    else if (x <= 1919810)
        cout << "very hard";
    else
        cout << "can not imagine";
    return 0;
}

B

思路:首先我们可以对每一个数求一下约数,因为这个数本身就是它所有约数的倍数。然后我们用一个二维的vector在每一个约数后面都记下当前的下标,表示当前下标的对应的数是这个约数的倍数。
然后我们在查找的时候用upper_bound函数求一下比当前下标x大的下标在vector中的下标,然后用对应的倍数的总和(及这个数vector对应的size)减去比当前下标x大的数的下标即可。

#include 
using namespace std;

const int N = 4e5 + 10;
vector<int> v[N];
int a[N];

void get(int idx, int n)
{
	for (int i = 1; i <= n / i; i++)
	{
		if (n % i == 0)
		{
			if (n / i != i)
				v[n / i].push_back(idx);
			v[i].push_back(idx);
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		get(i, a[i]);
	}
	for (int i = 1; i <= m; i++)
	{
		int op, x;
		cin >> op >> x;
		if (op == 1)
		{
			a[++n] = x;
			get(n, x);
		}
		else
		{
			int num = a[x];
			int cnt = upper_bound(v[num].begin(), v[num].end(), x) - v[num].begin();
			cout << v[num].size() - cnt << "\n";
		}
	}
	return 0;
}

C

思路:这个题的话我们手算一下的话,我们越大的数往中间放它的总和是在越大的,我们以最外层的举例最外层的话,只会被加一次,类似下面这个图1和2到最后就参与了一次加和计算。每个数上面的数表示它是由谁加和而来。
2023牛客寒假算法基础集训营6_第1张图片
知道这个规律之后我们发现数据是1e3的然后暴力的话是O(n^2)的级别那也就是1e6完全可以通过,我们就直接暴力即可。

#include 
using namespace std;

const int N = 1e3 + 10, mod = 1e9 + 7;
int a[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin >> n;
    int l = 1;
    for (int i = 1; i <= n; i += 2)
        a[n][l++] = i;
    for (int j = n; j >= 1; j--)
        if (j % 2 == 0)
            a[n][l++] = j;
    long long ans = 0;
    int m = n - 1;
    for (int k = m; k >= 1; k--)
        for (int i = 1; i <= k; i++)
            a[k][i] = (a[k + 1][i] + a[k + 1][i + 1]) % mod;
    cout << a[1][1] % mod << "\n";
    for (int i = 1; i <= n; i += 2)
        cout << i << ' ';
    for (int j = n; j >= 1; j--)
        if (j % 2 == 0)
            cout << j << ' ';
    return 0;
}

D

思路:这个题的话我们可以分成改d和改u两种情况,然后开一个全局的Max比较一下删去哪个后“udu”减少的最多,如果Max被更新的话,我们就记录一下此时的下标。

改d的话我们直接枚举即可,用d前面u的个数乘以d后面的u的个数就是删除这个d我们减少的“udu”的数量。

那么删除u的话,枚举肯定会T,但是我们发现删除第一个或者最后一个的话我们删除的肯定是最多的,因为此时我们u对应的“udu”肯定是最多的。

#include 
using namespace std;

#define int long long

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	string s;
	cin >> s;
	int cnt = 0;
	int idx = 0, flag = 0;
	int idx2 = 0;
	for (int i = 0; i < (int)s.size(); i++)
	{
		if (s[i] == 'u')
		{
			idx2 = i;
			cnt++;
		}
		if (s[i] == 'u' && flag == 0)
		{
			idx = i;;
			flag = 1;
		}
	}
	int sum = 0;
	int Max = 0;
	int id = 0;
	for (int i = 0; i < (int)s.size(); i++)
	{
		if (s[i] == 'u')
			sum++;
		if (s[i] == 'd')
		{
			int h = sum * (cnt - sum);
			if (Max < h)
			{
				Max = h;
				id = i;
			}
		}
	}
	sum = 0;
	int tot = 0;
	for (int i = idx + 1; i < (int)s.size(); i++)
	{
		if (s[i] == 'u')
			sum++;
		if (s[i] == 'd')
		{
			tot += cnt - sum;
		}
	}
	if (Max < tot)
	{
		Max = tot;
		id = idx;
	}
	tot = 0;
	sum = 0;
	for (int i = idx2 - 1; i >= 0; i--)
	{
		if (s[i] == 'u')
			sum++;
		if (s[i] == 'd')
		{
			tot += cnt - sum;
		}
	}
	if (Max < tot)
	{
		Max = tot;
		id = idx2;
	}
	for (int i = 0; i < (int)s.size(); i++)
	{
		if (i == id)
			cout << 'a';
		else
			cout << s[i];
	}
	return 0;
}

F

思路:这个题我们看题面的话第一时间想到的是二分,但是二分T掉了。

我们仔细读一下题面,然后考虑一下f函数,对于任何一个小于等于1e9的数来说,我们最多进行4次就可以把那个数变成1,那就说明我们预处理的话最多只用预处理2e54也就是8e5个数,然后只要是k大于4n的话,我们一定可以把所有的数全部变成1,那么我们直接输出1就好。

那么k小于4*n的话我们怎么预处理呢,我们先把数组从大到小排序,然后用一个大根堆维护一下操作k次时,我们的最大值是多少。首先我们把最大值取出来然后pop掉,然后对最大值进行一次操作然后再放回大根堆里,此时的堆顶就是我们进行k次操作后的最大值,用一个map记录一下即可。

#include 
using namespace std;

priority_queue<int, vector<int>, less<int>> heapb;
const int N = 2e5 + 10;
int a[N];
int n, k, q;
unordered_map<int, int> ans;

int f(int num)
{
	int count = 0;
	while (num)
	{
		num = num & (num - 1);
		count++;
	}
	return count;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	cin >> n >> q;
	int Max = 1;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		heapb.push(x);
	}
	for (int i = 1; i <= 4*n; i++)
	{
		int ma = heapb.top();
		heapb.pop();
		int g = f(ma);
		heapb.push(g);
		ans[i] = heapb.top();
	}
	while (q--)
	{
		int k;
		cin >> k;
		if (k >= 4 * n)
		{
			cout << 1 << "\n";
			continue;
		}
		cout << ans[k] << "\n";
	}
	return 0;
}

G

思路:我们可以先对数组从小到大排一下序,我们考虑双指针,如果i对应的两个数相乘大的话,我们就移动指针i,如果j对应的两个数相乘大的话,我们就移动指针j。

#include 
using namespace std;

const int N = 2e5 + 10;
int a[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    long long ans = 0;
    for (int i = 1, j = n; i < j ; )
    {
        k--;
        if (a[i]*a[i + 1] >= a[j]*a[j - 1])
            ans += a[i] * a[i + 1], i += 2;
        else
            ans += a[j] * a[j - 1], j -= 2;
        if (k == 0)
            break;
    }
    cout << ans << "\n";
    return 0;
}

H

思路:签到算一下概率即可

#include 
using namespace std;

int main()
{
    double x, l, r;
    scanf("%lf%lf%lf", &x, &l, &r);
    if (l >= x)
    {
        printf("%.10lf", (double)0);
    }
    else if (x > r)
    {
        printf("%.10lf", (double)1);
    }
    else
    {
        double k = x - l;
        double ans = k / (r - l + 1);
        printf("%.10lf", (double)ans);
    }
    return 0;
}    

I

思路:这个题的话我们考虑一个策略就是我们走完一条路就把我们刚刚走过的那条路去掉,让我们将要走的那条路变成1。

首先我们假设所有的路都是1的话我们可以用最朴素的bfs将从1走到n的最短路算出来。

那么现在呢会出现两种情况。一种是最短路大于m,另一种是等于m。不可能出现小于的情况,因为我们的最短路不可能小于路的总条数。

我们看一下大于m的情况,那么我们走当前的路时前面至少有一条路可以被去掉。

等于m的情况的时候,我们会发现当我们走第一条路时前面没有任何一条路可以被去掉,所以我们只能按原始的权值来计算。那么我们就把第一条路的权值记一下,最后用我们bfs算出的值减1,然后杀伤第一条路的权值即可。

#include 
using namespace std;

const int N = 2e5 + 10;
int step[N], st[N];
vector<int> mp[N];

void bfs(int u)
{
    queue<int> q;
    q.push(u);
    st[u] = 1;
    step[u] = 0;
    while (q.size())
    {
        int now = q.front();
        q.pop();
        for (int i = 0; i < (int)mp[now].size(); i++)
        {
            if (!st[mp[now][i]])
            {
                step[mp[now][i]] = step[now] + 1;
                st[mp[now][i]] = 1;
                q.push(mp[now][i]);
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n, m;
    cin >> n >> m;
    int num = 0;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        if (a == 1 || b == 1)
            num = c;
        mp[a].push_back(b);
        mp[b].push_back(a);
    }
    bfs(1);
    if (step[n] < m)
        cout << step[n] << "\n";
    else
        cout << step[n] - 1 + num << "\n";
    return 0;
}

你可能感兴趣的:(训练赛补题,算法,c++,图论)