个人感觉质量很不错的一套题,难度适中很适合我这种小白去做。不过由于在下能力有限,本文只会讲我通过的那些题。在难度上AHIK--FG--CB,接下来我会按这个难度顺序讲解。
A 造数
我们模拟一下它从n到0的过程,要让n变小,肯定是在n>2的时候不断向下除以2,我们假设一个数4到9,正着来就是*2再+1,那么倒过来就是-1再/2。偶数则直接除以2,最后特判一下2的时候直接减就行了。
点击查看代码
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,cnt=0;
cin>>n;
while(n>0)
{
cnt++;
if(n==2) n-=2;
if(n%2) n--;
else n/=2;
}
cout<<cnt;
}
H 两难抉择
第一个操作肯定选择加n会让总和最大,第二个操作选择最大的数让它×n最大。然后输出这两个的最大值就行了。
点击查看代码
void solve()
{
int sum=0;
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
sum+=x;
ve.push_back(x);
}
sort(ve.begin(),ve.end());
int mx=ve[ve.size()-1];
cout<<max(sum-mx+mx*n,sum+n);
}
I 除法移位
需要注意这题的除法不是向下取整,而是1/2=二分之一这样的正常除法。所以对原式就有:S=a1/a2a3...an。就是只有第一位是分子,其它都是分母。那么我们对每个数都有让它作分子也就是移到第一位需要的操作数,遍历一遍找到在允许的操作数内能找到的最大分子即可。
点击查看代码
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
int t=(n-i+1)%n;
ve.push_back({a[i],t});
}
int res=0,id=0;
sort(ve.begin(),ve.end());
for(auto x:ve)
{
int num=x.first,cnt=x.second;
if(cnt<=q)
{
if(num>res)
{
res=num;
id=cnt;
}
else if(num==res)
{
id=min(cnt,id);
}
}
}
cout<<id;
}
K 图上计数(Easy)
考虑到所有图都可以无限拆分再重组,那么我们把所有图都拆成点数为1的单位图。那么我们就可以得到n个独立的点,这n个点可以随意组装。那么最大代价就是ab,而a+b==n。易得当a,b越接近时S=ab最大。也就是a=n/2,b=n-a。
点击查看代码
void solve()
{
int n;
cin>>n;
cout<<n/2*(n-n/2);
}
F 两难抉择新编
注意到1-n/i是个n/1+n/2+n/3+...+n/n的调和级数。所以直接暴力遍历即可。这里用到了异或的运算法则。设sum = a ^ b,那么sum ^ a = b。所以存入数组时作sum为异或和,暴力时异或掉a[i],然后在异或a[i]*k即可。
点击查看代码
void solve()
{
int sum=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum^=a[i];
}
int ans1=sum,ans2=sum;
for(int i=1;i<=n;i++)
{
int mx=n/i;
for(int j=1;j<=mx;j++)
{
int k=sum^a[i];
ans1=max(ans1,k^(a[i]+j));
}
}
for(int i=1;i<=n;i++)
{
int mx=n/i;
for(int j=1;j<=mx;j++)
{
int k=sum^a[i];
ans2=max(ans2,k^(a[i]*j));
}
}
cout<<max(ans1,ans2);
}
G 旅途的终点
这道题贪心或者二分答案都可以。以下给出两种做法,先是贪心,就用类似反悔贪心的思想。先把前K个数当成使用了能力的位置。然后在k+1开始,开一个小根堆,每次先存入当前数,这样每次弹出的肯定是消耗生命最少的点。将伤害累加起来,如果此时你的生命不够负担这些伤害了就结束,否则继续往前走。
点击查看代码
void solve()
{
priority_queue<int,vector<int>,greater<int>>q;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
if(k>=n) {
cout<<n;
return ;
}
for(int i=1;i<=k;i++) q.push(a[i]);
int sum=0;
for(int i=k+1;i<=n;i++)
{
q.push(a[i]);
sum+=q.top();
q.pop();
if(sum>=m)
{
cout<<i-1;
return ;
}
}
cout<<n;
}
二分的思路就是二分答案,因为你肯定越往前走掉的血越多,那么答案是具有单调性的。check过程就是对于你选择的前x个数字,大的用技能消掉,剩下的扣血,如果血没扣完就说明这个答案成立,否则不行。
点击查看代码
bool check(int x)
{
vector<int> ve;
for(int i=1;i<=x;i++) ve.push_back(a[i]);
sort(ve.begin(),ve.end(),greater<int>());
int sum=0;
for(int i=k;i<x;i++) {
sum+=ve[i];
if(sum>=m) return 0;
}
return 1;
}
void solve()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int l=0,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l;
}
C 有大家喜欢的零食吗
这个其实没啥好说的,就是二分图最大匹配的板子题。大家若是不知道这个算法可以去学一下。就是小孩去匹配零食,每个人都有几种自己喜欢的,你得怎么分才能让更多的小孩拿到自己喜欢的零食。套个板子就行。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 521;
int nv[N],n;
bool vis[N];
vector<int> h[N];
bool dfs(int u)
{
for(auto v:h[u])
{
if(vis[v]) continue;
vis[v]=1;
if(!nv[v]||dfs(nv[v])) {
nv[v]=u;
return 1;
}
}
return 0;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++){
int s;
cin>>s;
while(s--)
{
int v;
cin>>v;
h[i].push_back(v);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
if(dfs(i)) ans++;
}
if(ans==n) cout<<"Yes";
else cout<<"No"<<endl<<n-ans;
}
B 爱探险的朵拉
感觉挺不错的一个题。就是从i可以走到a[i],问你怎么走走的点最多。我们可以把作一条从i到a[i]的有向边。那么问题就转换成了从一个图上,找一条最长子链。因为i是从1到n没有重复的,也就是说对于任意一个点,它的出度是1。如果该图没有环只有一个子链我们很好解决,就是记忆化搜索一下找到最长子链即可。但如果形成环的话,那么它就是一个内向基环树,普通的记忆化搜索对于1->2->3->1搜出的长度是1,2,3但实际上它们每个大小都是3。那么我们可以先用SCC缩点,将环缩成一个点,那个点的大小就是这个环里点的个数。然后再记忆化搜索即可。或者是找到每个基环树中环的大小,然后加上它树上最长子链长度就是答案,这里给出缩点后记忆化搜索的做法。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+20;
int n,d[N],res=0;
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],siz[N],cnt;
vector<int> h[N];
bool vis[N];
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
stk[++top]=x,instk[x]=1;
for(auto v:h[x])
{
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(instk[v]) low[x]=min(low[x],dfn[v]);
low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x]){
int y;
++cnt;
do{
y=stk[top--];
instk[y]=0;
scc[y]=cnt;
++siz[cnt];
}while(y!=x);
}
}
int dfs(int u)
{
vis[u]=1;
if(d[u]) return d[u];
d[u]++;
for(auto v:h[u])
{
if(vis[v]) continue;
vis[v]=1;
d[u]=max(d[u],dfs(v)+1);
}
return d[u];
}
signed main()
{
memset(d,0,sizeof d);
cin>>n;
for(int i=1;i<=n;i++){
int v;
cin>>v;
h[i].push_back(v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++){
int k=scc[i];
if(siz[k]>1) d[i]=siz[k];
}
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
res=max(res,dfs(i));
}
cout<<res;
}