2022 CCPC 威海 ACEGJ

发布时间 2023-10-06 21:27:09作者: nannan4128

2022 China Collegiate Programming Contest (CCPC) Weihai Site ACEGJ

A. Dunai 思维

题意:之前有\(n\)场比赛,有\(n\)个冠军队伍,每个队伍5个人。接下来给你\(m\)个即将参加比赛的人和所在位置(1~5)。问你在保证一个队伍至少有一个冠军在,并且每个位置都要有人。问最多能组成多少个队伍。

思路:我们考虑木桶原理,肯定是最少的那个位置决定最终的。但是又有考虑每个队伍都要有至少一个冠军,那我们贪心的去放,一个冠军放在一个队伍。那么最后的答案就是:\(min(冠军数,最少人数的位置的人数)\)

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int n,m;
    cin>>n;
    map<string,bool>mp;
    map<int,int>c1,c2;
    for(int i = 1;i <= n; i++)
    {
        for(int j = 1;j <= 5; j++)
        {
            string x; cin>>x;
            mp[x] = true;
        }
    }
    cin>>m;
    for(int i = 1;i <= m; i++)
    {
        string x;
        int pos;
        cin>>x>>pos;
        
        if(mp.count(x))
            c1[pos]++;
        c2[pos]++;
    }
    int mn = 1e9,ans = 0;
    for(int i = 1;i <= 5; i++)
        mn = min(mn,c2[i]);
    for(int i = 1;i <= 5;i++)
        ans += c1[i];
    cout<<min(ans,mn)<<"\n";
    return 0;
}

C. Grass 计算几何

题意:给你\(n\)个点,问你是否能找到5个点,使得在确定这5个点中某一个点为中心点之后其他点到它的连线中不存在别的另外4个中的公共点。

img

比如上图:\((1)\)符合题意,但是 \((2)\)不符合,因为对于\(AE,AC\)它们除了\(A\)还存在\(E\)这个公共点。

思路:考虑什么情况下是一定不存在的?当且仅当这\(5\)个点都共线。我们可以从斜率的角度考虑。

我们可以考虑随便找\(4\)个点,假设找一开始的\(4\)个点。然后去枚举第\(5\)个点。如果这\(5\)个点不是共线的,那么一定可以满足条件。我们再对于这\(5\)个点去枚举中心点去\(check\)即可。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        vector<array<int,2>>v;
        vector<int>res;
        for(int i = 1;i <= n; i++)
        {
            int x,y; cin>>x>>y;
            v.push_back({x,y});
        }
        auto invalid = [&]()//如果一开始都共线,或者点数小于5那肯定是不行的,否则一定存在
        {
            int sz = v.size();
            if(sz<5)return true;
            set<array<int,2>>s;
            for(int i = 1; i < sz; i++)
            {
                int tx = v[i][0]-v[0][0],ty = v[i][1]-v[0][1];
                int g = abs(__gcd(tx,ty));
                if(tx<0)tx = -tx,ty = -ty;
                tx/=g,ty/=g;
                s.insert({tx,ty});
            }
            return s.size() == 1;
        };
        auto valid_p = [&](int p)//看枚举的第五个点是否合法
        {
            set<array<int,2>>s;
            for(int i = 0; i < 4; i++)
            {
                int tx = v[i][0]-v[p][0],ty = v[i][1]-v[p][1];
                int g = abs(__gcd(tx,ty));
                if(tx<0)tx = -tx,ty = -ty;//注意统一负号,不然同侧和异侧会被当成是不同的
                tx/=g,ty/=g;
                s.insert({tx,ty});
            }
            return s.size() != 1;
        };
        if(invalid())
        {
            cout<<"NO\n";
            continue;
        }
        for(int i = 0;i < 4; i++)
            res.push_back(i);
        for(int i = 4;i < n; i++)
        {
            if(valid_p(i))
            {
                res.push_back(i);
                break;
            }
        }
        int cent = 0;
        for(int i = 0;i < 5; i++)
        {
            int cx = v[res[i]][0],cy = v[res[i]][1];
            set<array<int,2>>s;
            for(int j = 0; j < 5; j++)
            {
                if(i==j)continue;
                int tx = v[res[j]][0]-cx,ty = v[res[j]][1]-cy;
                int g = abs(__gcd(tx,ty));
                tx/=g,ty/=g;
                s.insert({tx,ty});
            }
            if(s.size()==4)//如果斜率有4种,说明是合法的(因为我们不同方向是不一样的,那么异侧的情况就合法,同侧不合法)
            {
                cent = res[i];
                break;
            }
        }
        cout<<"YES\n";
        cout<<v[cent][0]<<" "<<v[cent][1]<<"\n";
        for(int i = 0;i < 5; i++)
        {
            if(res[i]==cent)continue;
            cout<<v[res[i]][0]<<" "<<v[res[i]][1]<<"\n";
        }
    }
    return 0;
}

E. Python Will be Faster than C++

题意:给你\(n\)\(Python\)的版本和运行速度,以及\(C\)++的运行速度。对于未来版本\((>n)\),我们的递推柿子是\(a_i = max(0,2a_{i-1}-a{i-2})\)

思路:如果前\(n\)个有比\(k\)小的就直接输出,否则就开始递推。注意如果\(a[n]<a[n+1]\)(即递增的),那么只会越来越大。如果前面\(n\)个不满足,后面肯定更不满足的。我们考虑递推\(1e7\)以内的,如果都不行,那么就永远不可能。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e7 + 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];
    for(int i = 1;i <= n; i++)
    {
        if(a[i]<k)
        {
            cout<<"Python 3."<<i<<" will be faster than C++\n";
            return 0;
        }
    }
    int idx = n+1;
    while(idx<=1e7&&a[n-1]>a[n])
    {
        a[idx] = max(0,2*a[idx-1]-a[idx-2]);
        if(a[idx]<k)
        {
            cout<<"Python 3."<<idx<<" will be faster than C++\n";
            return 0;
        }
        idx++;
    }
    cout<<"Python will never be faster than C++\n";
    return 0;
}

G. Grade 2

题意:求\(\sum_{k = l}^{r}[\gcd(kx⊕x,x)=1]\)

思路:打表,发现有循环节。

打表代码:

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int x; cin>>x;
    cout<<"x = "<<x<<"\n";
    for(int k = 1;k <= 100; k++)
    {
        cout<<"k = "<<k<<" res = "<<(__gcd(k*x^x,x)==1)<<"\n";
    }
    cout<<"\n";
    return 0;
}

对于\(x = 15\)有(其中0表示满足条件):

image

对于\(x = 3\)

image

多试几个发现:循环节长度是第一个大于\(x\)的2的幂次。

对于题目\(\sum_{l}^{r}\)我们可以拆成\(\sum_{1}^{r}-\sum_{1}^{l-1}\)

那么长成这样可以考虑用前缀和处理,对于一个循环节里面,我们可以处理出前\(i\)个有多少个\(1\)\(pre[i]\)

然后我们的答案就是完整的循环节+多出来的部分。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e6 + 10;
ll x,n,l,r,pre[N];

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>x>>n;

    ll t = x,c= 1;
    while(t)
        t/=2,c*=2;

    for(int i = 1;i <= c; i++)
        pre[i] = pre[i-1]+(__gcd(i*x^x,x)==1);
    

    for(int i = 1;i <= n; i++)
    {
        cin>>l>>r;
        l--;
        ll res = 0;
        res -= (l/c)*pre[c];
        res -= pre[l%c];
        res += (r/c)*pre[c];
        res += pre[r%c];
        cout<<res<<"\n";
    }    
    return 0;
}

J. Eat, Sleep, Repeat

题意:给你\(n\)个数字,\(a_1\)\(a_n\),然后再给你\(k\)个限制,表示\(x_i\)的出现次数不能超过\(y_i\)个。

\(P\)\(F\)轮流操作,每次选一个数\(-1\)。当且仅当无论选哪个数\(-1\)都不满足限制或者全都是\(0\)的时候输。\(P\)是先手,问最后谁赢?

思路:我们考虑最后最多的操作次数,然后看是奇数还是偶数即可(奇数先手赢)。

我们考虑用\(map\)来记录限制,当限制变成\(0\)了,那么不可能再跨越。我们以\(0\)作为分界,找它的最大可能操作的变化。变完以后记得限制要改变。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;
        for(int i = 1;i <= n; i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        map<ll,int>mp;
        set<ll>s;
        for(int i = 1;i <= k; i++)
        {
            int x,y;
            cin>>x>>y;
            mp[x] = y;
            if(y==0)s.insert(x);
        }
        s.insert(-1),s.insert(1e18);//手动加入上下界
        for(int i = 1;i <= n; i++)
        {
            if(mp.count(a[i])==0)
                mp[a[i]] = n;
        }
        if(mp.count(0)==0)
            mp[0] = n;
        // cout<<"mp:\n";
        // for(auto [x,c]:mp)
        //     cout<<x<<" "<<c<<"\n";
        int cnt = 0;
        for(int i = 1;i <= n; i++)
        {
            int x = a[i];
            auto p = lower_bound(s.begin(),s.end(),x);
            p = prev(p);
            // cout<<"p:";
            // cout<<*p<<"\n";
            if(*p==-1){
                cnt += x,mp[0]--;
                if(mp[0]==0)s.insert(0);
            }else{
                cnt += x-(*p+1);
                if(mp.count(*p+1)){
                    mp[*p+1]--;
                    if(mp[*p+1]==0)s.insert(*p+1);
                }
            }
        }
       //cout<<"cnt = "<<cnt<<"\n";
        if(cnt%2)cout<<"Pico\n";
        else cout<<"FuuFuu\n";
    }
    return 0;
}