牛客小白月赛80 做题记录
A 矩阵快速幂签到
纯纯诈骗题,直接输出 \(n+1\) 然后取模就行。
B 又一次放学
\(n\) 个学生,第 \(i\) 个学生属于第 \(a_i\) 个班,求跑出 \(k\) 个学生之后最多有多少学生是同一个班的。
思路:开桶记录一下学生分属的班级,然后从按班级里学生人数从大到小排个序倒着接减,数量减到 \(k\) 个之后,第一个桶的数量就是答案。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x;
scanf("%lld",&x);
return x;
}
const int N=1e5+10;
int a[N],n,m,k,cnt[N],mx;
bool cmp(int x,int y)
{
return x>y;
}
signed main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;++i) a[i]=read(),cnt[a[i]]++,mx=max(mx,a[i]);
sort(cnt+1,cnt+1+mx,cmp);
for(int i=mx;i>=1;i--)
{
while(cnt[i]>0&&k>0)
{
cnt[i]-=1;
k-=1;
}
if(k==0)
{
cout<<cnt[1]<<"\n";
return 0;
}
}
return 0;
}
C&D 又放学辣
\(n\) 学生,\(m\) 个班级。第 \(i\) 个人属于第 \(a_i\) 个班级。跑掉 \(k\) 名学生。拖堂班级没有任何学生离校。求假设恰好只有班级 \(j,j \in [1,m]\) 的老师还在拖堂,在剩下的未拖堂的班级中,留在学校的人数最多的班级的最少的可能人数是多少。
C:考虑二分答案,因为 \(n,m\) 比较小,可以暴力的枚举每个班的人数然后进行二分。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc k<<1
#define rc k<<1|1
#define re register
const int N=5e4+10,mod=998244353;
int read()
{
int x;
scanf("%d",&x);
return x;
}
void prt(int x,int op)
{
if(op==1) cout<<x<<" ";
if(op==2) cout<<x<<"\n";
}
int n,m,k,mx;
int cnt[N];
bool check(int xx,int x,int cur)
{
int dis=cnt[cur]-x,res=k-x;
for(int i=1;i<=n;++i)
{
if(i==cur) continue;
if(i==xx) continue;
if(cnt[i]>dis)
{
if(res<(cnt[i]-dis)) return 0;
res-=(cnt[i]-dis);
}
}
return 1;
}
int sol(int x)
{
if(n-cnt[x]<k) return -1;
mx=-1;
for(int i=1;i<=m;++i)
{
if(i==x) continue;
int l=0,r=k,mid,ans=-1;
while(l<=r)
{
mid=(l+r)>>1;
if(check(x,mid,i)) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==-1) continue;
mx=max(mx,cnt[i]-ans);
}
return mx;
}
void mian()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;++i)
{
int x=read();
cnt[x]++;
}
for(int i=1;i<=m;++i) printf("%d ",sol(i));
}
int main()
{
mian();
return 0;
}
D:因为 \(n,m\) 比较大,考虑更优的做法。预处理出人数最多的班级人数为 \(x\) 时走的人数,对于每次询问,二分人数最多的班级的人数,判断是否合法即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x;
scanf("%lld",&x);
return x;
}
const int N=1e5+10;
int n,m,k,mx,l,r,ans,tmp,mid,sm,a[N],f[N],cnt[N];
signed main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;++i) a[read()]+=1,sm+=1;
for(int i=1;i<=m;++i)
{
mx=max(mx,a[i]),cnt[a[i]]+=1;
}
for(int i=mx-1;i>=0;--i)
{
f[i]=f[i+1]+cnt[i+1];
cnt[i]+=cnt[i+1];
}
//f[x] 人数最多的班级人数为 x 时走的人数
for(int i=1;i<=m;++i)
{
l=0,r=mx,ans=-1;
if(sm-a[i]>=k)
{
while(l<=r)
{
mid=(l+r)>>1;
if(a[i]>mid) tmp=a[i]-mid;
else tmp=0;
if (f[mid]-tmp<=k) ans=mid,r=mid-1;
else l=mid+1;
}
}
cout<<ans<<"\n";
}
return 0;
}
E 一种因子游戏
Alice 和 Bob 每人有 \(n\) 张牌,两个人都知道对方手里的所有牌上的数字。规则如下:
每回合,Alice 先挑自己手里牌中一张打出。然后 Bob 再从自己手里牌中挑一张打出。如果 Bob 打出的牌与 Alice 打出的牌上的数字不互质,则Alice获胜,游戏结束;否则继续进行下一回合,若双方都没有牌了,则 Bob 获胜。两人都足够聪明。现在判断最后谁能获胜。
思路:感觉像博弈论,其实又是诈骗题,因为 Bob 足够聪明,所以可以考虑将两人牌中互质的牌连边,判断是否可以构成完美匹配,若构成完美匹配,即最大匹配数为 \(n\) 则双方没牌,Bob 获胜,否则 Alice 获胜。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc k<<1
#define rc k<<1|1
#define re register
const int N=5e4+10,mod=998244353;
int read()
{
int x;
scanf("%d",&x);
return x;
}
void prt(int x,int op)
{
if(op==1) cout<<x<<" ";
if(op==2) cout<<x<<"\n";
}
int mp[501][501];
int n,vis[N],match[N];
bool dfs(int u)
{
for(int i=1;i<=n;++i)
{
if(mp[u][i]&&!vis[i])
{
vis[i]=1;
if(!match[i]||dfs(match[i]))
{
match[i]=u;
return 1;
}
}
}
return 0;
}
int ans;
int mat()
{
for(int i=1;i<=n;++i)
{
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
return ans;
}
int a[N],b[N];
int main()
{
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i)
{
b[i]=read();
for(int j=1;j<=n;++j)
{
if(__gcd(a[j],b[i])==1) mp[j][i]=1;
}
}
if(mat()==n) puts("Bob");
else puts("Alice");
return 0;
}
F 一种异或游戏
还没看,有空再写,咕咕咕~