目录
A、矩阵快速幂签到
赛题链接:A-矩阵快速幂签到_牛客小白月赛80 (nowcoder.com)
思路:签到题,
公式如下:
由
可得:
因此可知 是一个等差数列。由 可知 。
注意要取模!!!
代码如下:
#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)
思路:
典型二分,遍历每个不下课的班级,然后二分人数最多的班级人数。将二分值输出即可。
时间复杂度:
代码如下:
#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函数里面的循环在外面进行预处理,这里预处理用到了前缀和与二分。
时间复杂度:
代码如下:
#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赢。
时间复杂度:
代码如下:
#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;
}