• AcWing第一场周赛题解


    AcWing第一场周赛题解

    A.选择数字

    题目链接:AcWing 3577. 选择数字

    比最大值还大的值一定不再数组中,所以直接输出两个数组的最大值即可。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, m;
    int x, a, b;
    
    int main()
    {
        cin >> n;
        for(int i = 0; i < n; i++)
        {
            cin >> x;
            a = max(a, x);
        }
        cin >> m;
        for(int i = 0; i < m; i++)
        {
            cin >> x;
            b = max(b, x);
        }
    
        cout << a << ' ' << b << endl;
    
        return 0;
    }
    

    B.最大中位数

    题目链接:3578. 最大中位数

    比赛的时候脑袋发抽,老是想用什么奇奇怪怪的方法做,加上刚刚学了对拍,对了一个多小时,发现暴力都写错了,菜死了。。。赛后发现,直接暴力遍历就行

    首先肯定要排序的。排完序之后可以发现,从中位数开始,往后直接一个一个累加,并且一旦中位数和后面的数字相等了,就得一起同时累加,直到k无法满足将相等的数都加1为止。此时中位数即为最大中位数。(注意边界是2e9

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 2e5+10;
    
    int n, k;
    int a[N];
    
    int main()
    {
        cin >> n >> k;
        for(int i = 0; i < n; i++) cin >> a[i];
        sort(a, a + n);
        int p = n / 2;
        a[n] = 2e9+10;
        for(int i = p + 1; i <= n && k; i++)
        {
        	if(i - p <= k) 
        	{
        	    int t = a[i] - a[p];
        	    a[p] += min(t, k / (i - p)), k -= (i - p) * t;
        	}
        	else k = 0;
        }
    	cout << a[p] << endl;
        
        return 0;
    }
    

    法二:二分

    同样,我们只需要对排完后,中位数及其后面数进行操作。可以二分答案mid,将后半段小于mid的全部加到mid,然后判断操作是否小于k。二分出满足条件的最大值,即为最终答案。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 2e5+10;
    
    int n, k;
    int a[N];
    
    bool check(int p)
    {
        LL sum = 0;
        for(int i = n / 2; i < n; i++)
            if(a[i] < p) sum += p - a[i];
        if(sum > k) return false;
        else return true;
    }
    
    int main()
    {
        cin >> n >> k;
        for(int i = 0; i < n; i++) cin >> a[i];
        sort(a, a + n);
        
        int l = 0, r = 2e9;
        while(l < r)
        {
            int mid = (LL)l + r + 1 >> 1;
            if(check(mid)) l = mid;
            else r = mid - 1;
        }
    	cout << l << endl;
        
        return 0;
    }
    

    C.数字移动

    题目链接:3579. 数字移动

    经过分析可以发现,如果将每一个数字看成一个顶点,可以移动看成一条有向边,则可以构成一个有向图。而且该有向图中,每一个顶点只有一条出边和一条入边。可以回到自己原本位置的路经一定是一个简单环。由此可以得出做法:求该图的所有强连通分量,答案即为所在强连通分量的点的数目。

    进一步分析可以发现,所有的环可以看成一个集合,每一个数字需要的操作次数即为所在集合的大小。所以可以用维护集合大小的并查集做。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 2e5+10;
    
    int t, n;
    int p[N], s[N];
    
    int find(int x)
    {
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            scanf("%d", &n);
            for(int i = 1; i <= n; i++) p[i] = i, s[i] = 1;
            for(int j = 1; j <= n; j++)
            {
                int x;
                scanf("%d", &x);
                if(find(j) != find(x))
                {
                    s[find(j)] += s[find(x)];
                    p[find(x)] = p[find(j)];
                }
            }
            for(int i = 1; i <= n; i++)
                printf("%d ", s[find(i)]);
            puts("");
        }
        
        return 0;
    }
    
  • 相关阅读:
    学习总结(二十六)
    学习总结(二十五)
    在知乎学习怎么参加工作
    连分数系列
    Kalman Filter
    五子棋的学习
    Dijkstra
    三等分角、化圆为方、倍立方体
    女朋友走丢数学模型
    传染病模型
  • 原文地址:https://www.cnblogs.com/grain-rain/p/14826467.html
Copyright © 2020-2023  润新知