2022CCPC威海:A、C、E、G、I、J

2022CCPC威海:A、C、E、G、I、J

目前这5题让我觉着,威海这场思路倒不是多难,但是代码我觉着很难写,比如c和j。

Problem - A - Codeforces

问题解析

题目很怪,看了好多次。

题目说有个比赛,先给你n行,每行5个人,这五个人的顺序是他们比赛的位置(1~5),这n行是之前比赛的冠军,再给你m个人,每个人有对应的位置,问你最多能组成多少队伍参加比赛,每个队伍里都至少要有一个冠军,每个人的位置不能变。

一开始想的能组多少不同的队伍,这样就是每个位置的不同人数和冠军的数量来算,但是显然样例1就不对劲。后来发现是能组成的队伍数,不是不同的队伍数。

  • 那就计算每个位置的人数,如果不考虑冠军的因素,能组成的最多队伍数就等于这五个位置里人数最少的数量(比如1到4位置都有10人,但位置5只有1人,那就只能组成1个队伍,木桶原理)。
  • 如果要考虑冠军因素,那贪心的来想,就是每个队伍一个冠军,那能组成的队伍数就是冠军的数量。

答案就是两者中的最小值。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50, MOD = 1e9 + 7;

void solve()
{
    int n, m, x;
    string s;
    unordered_map<int, int>mymap, champion;
    unordered_map<string, int>st;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= 5; j++)
        {
            cin >> s;
            st[s] = 1;
        }
    }
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> s >> x;
        mymap[x]++;
        if (st.count(s))
        {
            champion[x]++;
        }
    }
    int mn = 1e9, ans = 0;
    for (int i = 1; i <= 5; i++)mn = min(mn, mymap[i]);
    for (int i = 1; i <= 5; i++)
    {
        ans += champion[i];
    }
    cout << min(mn,ans);
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    
    while (t--)
    {
        solve();
    }
    return 0;
}

Problem - C - Codeforces

问题解析

题目说给你n个点,问你能不能找到五个点ABCDE,让线段AB、AC、AD、AE之间不发生交叉。

很显然,要是给的点不够5个,就直接输出NO。

我们也可以想出,只要BCDE四个点不是都和A共线的,就能有结果,哪怕BCDE四个点都是共线。

在这里插入图片描述

也就说重点不在BCDE上,而是在点A上。

对于给的n个点,我们之间把前四个点记录下来,然后从剩下的点里面找第五个点。每次我们假设这五个点是答案送去判断,如果能组成答案就输出,不能我们就找下一个点当作第五个点,以此类推,要是找完了还没能出结果,说明没有答案,输出NO。

至于判断五个点是不是答案,我们枚举每个点当作是A,剩下的当作BCDE,然后看他们和A的共线情况。

这里我是判断他们和A的斜率:**(y2-y1)/(x2-x1)**是否相同。如果都不相同,说明当前枚举的点A就可以是一个合格的答案。

如果有两个斜率相同,说明这两个点和A共线,那么,当且仅当A是在这俩点中间时,才是正确的,比如:

在这里插入图片描述

而如果A不在两点之间,显然答案不对的,比如:

在这里插入图片描述

至于判断A是否在BC之间,只要看AB+AC是否等于BC即可。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 2e6 + 50, MOD = 1e9 + 7;

int gcd(int a, int b)
{
    return a == 0 ? b : gcd(b % a, a);
}

bool check(vector<PII>& ans)
{
    for (int i = 0; i < 5; i++)
    {
    	//记录下每个斜率的点
        unordered_map<double, vector<PII>>res;
        bool flag = true;
        for (int j = 0; j < 5; j++)
        {
            if (i == j)continue;
            //计算斜率:(y2-y1)/(x2-x1)
            double b = (ans[j].second - ans[i].second), a = (ans[j].first - ans[i].first);
            double c = b / a;
            //如果已经有两个点与A共线,那这第三个点显然不对
            if (res[c].size() >= 2)
            {
                flag = false;
                break;
            }
            //如果已经有一个点与A共线,那看A在不在这两个点之间
            else if (res[c].size() == 1)
            {
                int x = abs(ans[i].first - res[c][0].first) + abs(ans[i].second - res[c][0].second);
                int y = abs(ans[i].first - ans[j].first) + abs(ans[i].second - ans[j].second);
                int z = abs(ans[j].first - res[c][0].first) + abs(ans[j].second - res[c][0].second);
                if (x + y == z)
                {
                    res[c].push_back(ans[j]);
                }
                else
                {
                    flag = false;
                    break;
                }
            }
            else res[c].push_back(ans[j]);
        }
        if (flag)
        {
            cout << "YES" << endl;
            cout << ans[i].first << " " << ans[i].second << endl;
            for (int j = 0; j < 5; j++)
            {
                if (i == j)continue;
                cout << ans[j].first << " " << ans[j].second << endl;
            }
            return true;
        }
    }
    return false;
}

void solve()
{
    int n;
    cin >> n;
    unordered_map<double, int>mymap;
    vector<PII>v(n + 1), ans;
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i].first >> v[i].second;
        if (ans.size() < 4)
        {
        	//固定前四个点
            ans.push_back(v[i]);
        }
    }
    if (n < 5)
    {
        cout << "NO" << endl;
        return;
    }
    for (int i = 5; i <= n; i++)
    {
    	//找第五个点
        ans.push_back(v[i]);
        if (check( ans ))
        {
            return;
        }
        ans.pop_back();
    }
    cout << "NO" << endl;
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;

    while (t--)
    {
        solve();
    }
    return 0;
}

Problem - E - Codeforces

问题解析

题目是说给你一个长度为n的数组和一个数k,要你找到第一个小于k的数的位置,如果数组没有,那你可以扩展数组:ai=a(i-1)*2-a(i-2);

直到找到小于k的数位置,如果永远也找不到,输出永远也找不到。

没啥想法,就想着先把数组扩展到1e6个数,如果找到了就输出位置,找不到就输出永远也找不到。

然后就过了,笑死。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50, MOD = 1e9 + 7;

int a[N];
void solve()
{
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] < k)
        {
            cout << "Python 3." << i << " will be faster than C++" << endl;
            return;
        }
    }
    for (int i = n + 1; i <= 1000000; i++)
    {
        a[i] = max((int)0, 2 * a[i - 1] - a[i - 2]);
        if (a[i] < k)
        {
            cout << "Python 3." << i << " will be faster than C++" << endl;
            return;
        }
    }
    cout << "Python will never be faster than C++";
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    
    while (t--)
    {
        solve();
    }
    return 0;
}

Problem - G - Codeforces

问题解析

题目说给你一个数x和m次询问,每次询问给一个区间l~r,问你l到r里有几个数k能够满足gcd((k*x ^ x),x)=1.

找规律题,对于数字x,它的答案其实是有一个长度为2^(log2(x))的循环节(循环节长度就是第一个大于x的2的次幂)。

因为x最大也就1e6,我们可以先把循环节的答案给算出来,一个循环节的k的个数我们记作cnt,然后每次询问,根据区间的长度能分成三部分:

  1. 最左边的部分,这一部分不是一个完整的循环节。
  2. 中间的部分,包含了复数个循环节。
  3. 最右边的部分,这一部分也不是一个完整的循环节。

中间的部分,我们可以通过计算出循环节的个数*cnt得到结果。

至于左边和右边的部分,一开始我用for循环一个个找,然后t了,想了想最坏的情况每次询问我们都要搜1e6左右的长度,复杂度就变成n^2了,慢死。

我们在计算循环节的时候,顺便准备一个前缀和数组sum,sum[i]表示0到i有多少个结果。这样左边和右边的部分我们就能通过前缀和数组算出结果了。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 2e6 + 50, MOD = 1e9 + 7;

//mymap[i]表示循环节内i是不是正确的答案。
//sum是前缀和数组
int mymap[N], sum[N];
int gcd(int a, int b)
{
    return a == 0 ? b : gcd(b % a, a);
}
void solve()
{
    int x, n, y = 1, cnt = 0;
    cin >> x >> n;
    //求出循环节的长度
    while (y < x)
    {
        y *= 2;
    }
    //求出循环节内的答案数
    for (int i = 0; i < y; i++)
    {
        if (gcd((i * x)^x, x) == 1)
        {
            mymap[i] = 1;
            cnt++;
        }
        if (i == 0)sum[i] = mymap[i];
        else sum[i] = sum[i - 1] + mymap[i];
    }

    while (n--)
    {
        int l, r;
        cin >> l >> r;
        //l2是区间内第一个循环节的开头,r2是区间内最后一个循环节的结尾
        int l2 = ceil((double)l / y), r2 = r / y;
        int res = 0;
        if (l2 * y <= r2 * y)
        {
            res = (r2 - l2) * cnt;
            res += sum[r % y];
        }
        res += sum[min(l2 * y - 1, r) % y] - sum[(l - 1) % y];   
        cout << res << endl;
    }
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    
    while (t--)
    {
        solve();
    }
    return 0;
}

Problem - I - Codeforces

问题解析

题目是说要下龙蛋,每下一个龙蛋需要n种元素各a[i]个,现在有k种工具龙去收集元素,第i种工具龙能收集单类元素2^(i-1) 个,问你最多能下几个蛋。

能生x个蛋就肯定能生x-1个蛋,有可能能生x+1个蛋,这就有单调性了,于是二分答案。

把生mid个蛋所需要的各类元素数量都存入单调队列里,优先收集所需量多的元素,我们尽量选高级的工具龙去收集,每次要么一次性收集完当前元素,要么一次性用完当前工具龙,防止浪费,浪费的龙越少,能收集的元素就越多,能生的蛋可能就更多。

(二分上限的边界不好找,于是直接算了一下极限情况下这些工具龙能让我们生的最多蛋数来做上限)

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 5e4 + 50, MOD = 1e9 + 7;

int n, k, a[N], b[30], c[30];
bool check(int mid)
{
    priority_queue<int, vector<int>, less<int>>que;
    for (int i = 1; i <= n; i++)
    {
        que.push(a[i] * mid);
    }
    for (int i = 0; i < k; i++)c[i] = b[i];
    int idx = k - 1, ans = (1 << idx);
    while (!que.empty())
    {
        int res = que.top();
        que.pop();
        if (idx < 0)return false;
        int cnt = max((int)1, min(c[idx], res / ans));
        c[idx] -= cnt;
        res -= cnt * ans;
        if (res > 0)que.push(res);
        if (!c[idx])
        {
            idx--;
            ans = (1 << idx);
        }
    }
    return true;
}

void solve()
{
    int suma = 0, sumb = 0;
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        suma += a[i];
    }
    for (int i = 0; i < k; i++)
    {
        cin >> b[i];
        sumb += b[i] * (1 << i);
    }
    int l = 0, r = sumb / suma;
    while (l < r)
    {
        int mid = (l + r + 1) / 2;
        if (check(mid))l = mid;
        else r = mid - 1;
    }
    cout << l << endl;
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;

    while (t--)
    {
        solve();
    }
    return 0;
}

Problem - J - Codeforces

问题解析

题目是说给你n个数,以及m个限制,每个限制有两个数x和y,表示数x最多只能同时出现y个,每次两个玩家会选一个数-1,最先做不出操作的就输了。

一开始看见博弈吓得很,但是看了题目发现他们每轮的操作并没有多花哨,根据奇偶性就能求出结果。

按照限制中,y=0的地方分割数,如果x最多出现次数为0,说明比他大的数都只能停在x+1上了,不能更小。

然后剩下的数则能删多小删多小。

但是可能会出现x出现次数为0,x+1出现次数为1的限制。

(个人感觉思路不难,但是代码有点麻烦,文字不好说,看代码注释罢)

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50, MOD = 1e9 + 7;

void solve()
{
    int n, m, x, y;
    cin >> n >> m;
    //lim只记录y=0的情况
    vector<int>v(n), lim;
    //mymap记录每个数的限制,cnt记录操作之后每个数出现了几次
    unordered_map<int, int>mymap, cnt;
    for (int i = 0; i < n; i++)cin >> v[i];
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;
        mymap[x] = y;
        //如果y=0,则把当前x存入数组lim中
        if (y == 0)
        {
            lim.push_back(x);
        }
    }
    //为了方便,lim额外存上x=-1和x=(任意一个很大的数)的情况
    mymap[-1] = 0;
    lim.push_back(-1);
    mymap[1e18] = 0;
    lim.push_back(1e18);
    sort(v.begin(), v.end());
    sort(lim.begin(), lim.end());
    //res记录结果,ans记录当前能减小到的最小值,idx表示我们当前要减小的是哪个数,l表示当前是第几个限制。
    int res = 0, ans = 0, idx = 0, l = 0, len = lim.size();
    while (idx < n)
    {
    	//lim[l]当作上界,要保证当前要减小的数小于lim[l](所以要加一个很大的数
    	//如果当前进行操作的数大于lim[l],则l后移,并更新ans
        while (l < len && v[idx] > lim[l])
        {
        	//既然lim[l]出现次数为0,那么大家能减小到的最小值就为ans
            ans = lim[l] + 1;
            l++;

        }
        
        while (idx < n && v[idx] < lim[l])
        {
        	//如果ans的出现次数也有限制,那么当减小到ans的数到限制后,ans要++。
            while (mymap.count(ans) && cnt[ans] == mymap[ans])
            {
                ans++;
            }
            //计算能减小的次数
            res += v[idx] - ans;
            cnt[ans]++;
            idx++;
        }
    }
    //根据奇偶性判断输赢
    if (res % 2 == 1)cout << "Pico" << endl;
    else cout << "FuuFuu" << endl;
}

signed main()
{

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    
    while (t--)
    {
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值