牛客小白月赛80

目录

A、矩阵快速幂签到

B、第一次放学

C、又放学辣(简单)

D、又放学辣(进阶)

E、一种因子游戏

F、一种异或游戏


A、矩阵快速幂签到

赛题链接:A-矩阵快速幂签到_牛客小白月赛80 (nowcoder.com)

思路:签到题,

公式如下:

a_{n}=2*a_{n-1}-a_{n-2}

可得:a_{n}-a_{n-1}=2*a_{n-1}-a_{n-2}-a_{n-1}=a_{n-1}-a_{n-2}

因此可知 a_{n} 是一个等差数列。由a_{0}=1  a_{1}=2 可知 a_{n}=n+1

注意要取模!!!

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define PII pair<int,int>
#define endl "\n"
const int N=1e6+10;
const int INF=1e18;
const int mod=998244353;
void solve()
{
    int n;
    cin>>n;
    cout<<(n+1)%mod<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
//     cin>>T;
    while(T--)solve();
    return 0;
}

B、第一次放学

赛题链接:B-第一次放学_牛客小白月赛80 (nowcoder.com)

思路:签到题。

人数最多的班级人数为mx

当总人数减去mx>=k时,输出mx,否则输出n-k。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define PII pair<int,int>
#define endl "\n"
const int N=1e6+10;
const int INF=1e18;
int ban[N];
void solve()
{
    int n,m,k;
    cin>>n>>m>>k;
    int mx=0;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        ban[x]++;
        mx=max(mx,ban[x]);
    }
    if(n-mx>=k)cout<<mx<<endl;
    else cout<<n-k<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
//     cin>>T;
    while(T--)solve();
    return 0;
}

C、又放学辣(简单)

赛题链接:C-又放学辣(简单)_牛客小白月赛80 (nowcoder.com)

思路:

典型二分,遍历每个不下课的班级,然后二分人数最多的班级人数。将二分值输出即可。

时间复杂度: O(n^{2}logn) 

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long 
const int N=1e6+10;
int arr[N];
unordered_map<int,int>r;
int n,m,k;
bool check(int x,int t)
{
    int sum=0;
    for(auto i:r)
    {
        sum+=max(0ll,i.second-x);
    }
    if(r[t]>=x)sum-=(r[t]-x);
    if(sum<=k)return true;
    return false;
}
signed main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        r[arr[i]]++;
    }
    for(int i=1;i<=m;i++)
    {
        if(n-r[i]<k)
        {
            cout<<-1<<" ";
            continue;
        }
        int l=0,r=n;
        while(l<r)
        {
            int mid=(l+r)/2;
            if(check(mid,i))r=mid;
            else l=mid+1;
        }
        cout<<l<<" ";
    }
}

D、又放学辣(进阶)

赛题链接:D-又放学辣(进阶)_牛客小白月赛80 (nowcoder.com)

思路:在(简单)的基础上优化check函数,将check函数里面的循环在外面进行预处理,这里预处理用到了前缀和与二分。

时间复杂度:O(nlogn) 

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long 
const int N=1e6+10;
const int INF=1e18;
int arr[N],da[N],sum[N];
unordered_map<int,int>r;
int n,m,k;
bool check(int x,int t)
{
    int sum=0;
    if(r[t]>=x)sum=da[x]-(r[t]-x);
    else sum=da[x];
    if(sum<=k)return true;
    return false;
}
signed main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        r[arr[i]]++;
    }
    vector<int>aas;
    for(auto i:r)aas.push_back(i.second);
    sort(aas.begin(),aas.end());
    sum[0]=aas[0];
    for(int i=1;i<aas.size();i++)sum[i]=sum[i-1]+aas[i];
    for(int i=0;i<=n;i++)
    {
        int k=lower_bound(aas.begin(),aas.end(),i)-aas.begin();
        int ssm=sum[(int)aas.size()-1]-(k==0?0:sum[k-1]);
        if(k==(int)aas.size())da[i]=0;
        else da[i]=ssm-((int)aas.size()-k)*i;
    }
    for(int i=1;i<=m;i++)
    {
        if(n-r[i]<k)
        {
            cout<<-1<<" ";
            continue;
        }
        int l=0,r=n;
        while(l<r)
        {
            int mid=(l+r)/2;
            if(check(mid,i))r=mid;
            else l=mid+1;
        }
        cout<<l<<" ";
    }
}

E、一种因子游戏

赛题链接:E-一种因子游戏_牛客小白月赛80 (nowcoder.com)

思路:

首先将所有互质的数对进行连边,对于Bob来说,想要赢,就必须采取和Alice互质数对匹配最多的策略,自然而然就能想到这一题是典型的二分图最大匹配匹配。因此跑一边匈牙利算法找到最大匹配数,数量等于n,那么Bob赢,否则Alice赢。

时间复杂度:O(n^{3})

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define PII pair<int,int>
#define endl "\n"
const int N=1e6+10;
const int INF=1e18;
const int mod=998244353;
int u[N],v[N],ne[N],first[N],dis[N],book[N],d[N],arr[N],brr[N];
int idx=1;
void add(int a,int b)
{
    u[idx]=a,v[idx]=b,ne[idx]=first[u[idx]],first[u[idx]]=idx,idx++;
}
int panduan(int x)
{
    int k=first[x];
    while(k+1)
    {
        if(book[v[k]]==0)
        {
            book[v[k]]=1;
            if(d[v[k]]==0)
            {
                d[v[k]]=x;
                return 1;
            }
            else
            {
            	int p=d[v[k]];
                if(panduan(p))
                {
                    d[v[k]]=x;
                    return 1;
                }
            }
        }
        k=ne[k];
    }
    return 0;
}
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>arr[i];
    for(int i=1;i<=n;i++)cin>>brr[i];
    memset(first,-1,sizeof(first));
    set<int>ans;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(__gcd(arr[i],brr[j])==1)
            {
                add(i,j);
                ans.insert(i);
            }
        }
    }
    int sum=0;
    for(auto i:ans)
    {
        memset(book,0,sizeof(book));
        if(panduan(i))sum++;
    }
    if(sum==n)cout<<"Bob"<<endl;
    else cout<<"Alice"<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
//     cin>>T;
    while(T--)solve();
    return 0;
}

F、一种异或游戏

思路:(两种思路,一种是站在Alice角度上思考如何出牌,一种是Bob角度上如何最高效率接牌)

利用异或的性质,两个数a,b异或值为k,等价于a和b^k的值相等。

站在Alice的角度上,想要赢那么必须采取打完牌且Bob接不起的策略。

站在Bob的角度上,要Bob对于Alice手中能够接的上的牌保留一张,其余的牌全部和接不起的牌对出。

因此这题可以通过直接找出Alice手中Bob接不起的牌(异或值不为k)的数量sum

因此当sum+1>=min(n,m)时,那么Alice必胜,否则分情况讨论Alice手中的Bob接不起的牌和Bob手中接的起的牌保留一张剩余牌的数量进行大小对比,前者大那么Alice赢,否则Bob赢。

之所以是sum+1是因为题目强调:若某一方打完了所有手牌,则Alice直接获胜,不再进行异或值是否为 K 的判定!!!

注意:虽然数据在1e6,但是异或值可能大于1e6,因此数组开到1e7,否则段错误。

注意:要Bob对于Alice手中能够接的上的牌保留一张,其余的牌全部和接不起的牌对出。之前没考虑这种情况直接交了,但是过了,可能是牛客官方出题数据过小。。。感谢评论区网友的提醒,发现了这个问题。

吐槽:此题卡常,map过不了,可能就会导致很多人过不了。

错误代码如下:

HACK数据:

5 4 1
100 100 2 3 4
0 3 2 5

true stdout:Alice

false stdout:Bob

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define PII pair<int,int>
#define endl "\n"
const int N=1e7+10;
const int INF=1e18;
const int mod=998244353;
int a[N],b[N];
int n,m,k;
void solve()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        a[x]++;
    }
    for(int i=1;i<=m;i++)
    {
        int x;
        cin>>x;
        b[x^k]++;
    }
    int sum=0;
    for(int i=0;i<N;i++)if(!b[i])sum+=a[i];
    if(sum+1>=min(n,m))cout<<"Alice"<<endl;
    else cout<<"Bob"<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
//     cin>>T;
    while(T--)solve();
    return 0;
}

修改后正确代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define PII pair<int,int>
#define endl "\n"
const int N=1e7+10;
const int INF=1e18;
const int mod=998244353;
int a[N],b[N];
int n,m,k;
void solve()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        a[x]++;
    }
    for(int i=1;i<=m;i++)
    {
        int x;
        cin>>x;
        b[x^k]++;
    }
    int sum=0;
    int chu=0;
    for(int i=0;i<N;i++)
    {
        if(a[i]==0&&b[i])sum+=b[i];
        else if(a[i]&&b[i])sum+=(b[i]-1);
        else if(a[i]&&b[i]==0)chu+=a[i];
    }
    if(chu+1>=min(n,m))cout<<"Alice"<<endl;
    else 
    {
        if(chu>sum)cout<<"Alice"<<endl;
        else cout<<"Bob"<<endl;
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
//     cin>>T;
    while(T--)solve();
    return 0;
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值