牛客 小白 109 20250119
牛客小白月赛109_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
A:
题目大意:根据不等式求值
#include<bits/stdc++.h>using namespace std;int main()
{int n;cin>>n;if (n<=19375331) cout<<n;else cout<<19375332;return 0;
}
简单签到
B:
题目大意:给出一个序列,可以划分为两个子序列,判断从第一个子序列中选出 \(k_1\) 个元素的平均数能不能大于从第二个子序列中选出 \(k_2\) 个元素的中位数
#include<bits/stdc++.h>using namespace std;int a[100010];int main()
{int n,k1,k2;cin>>n>>k1>>k2;for (int i=0;i<n;i++) cin>>a[i];int mem=a[n-1];sort(a,a+n);if (a[n-1]==mem) cout<<"No";else cout<<"Yes";return 0;
}
诈骗题,首先要知道在一个序列 \(x\) 中设平均值为 \(a\),中位数为 \(b\) ,满足以下不等式
又因为子序列的最小元素个数为 \(1\) ,\(k_1,k_2\ge 1\),如果两个子序列都选出一个最大的元素(最优情况),只需要判断第一个子序列中能否存在一个原来序列中唯一的最大元素即可
C:
题目大意:给出一个 \(n\) ,每次给一个左右区间,将区间内的数依次标号,已经标号过的数不再重复标记,求第 \(k\) 个数的标号
#include<bits/stdc++.h>using namespace std;const long long mod=1e9+7;set<int> st;
int a[100010];
int cnt;int main()
{int n,q;cin>>n>>q;for (int i=1;i<=n+1;i++) st.insert(i); for (int i=0;i<q;i++){int op;cin>>op;if (op==1){int l,r;cin>>l>>r;int now=l;while(1){now=*st.lower_bound(now);if (now>r) break;a[now]=++cnt;st.erase(now);}}else{int x;cin>>x;cout<<a[x]<<endl;}}return 0;
}
初始化时将数组从左到右存进 set
中,表示没有被编号
- 操作 \(1\):利用二分查找,找到在
set
中并且在l,r
内的数,取出集合并编号,当找到下一个没有被编号的数在l,r
外时,退出循环查找 - 操作 \(2\):直接输出记录编号(没有被编号的数默认为 \(0\) )即可
D:
题目大意:
#include <bits/stdc++.h>using namespace std;int main()
{int n,m;long long sum=0;cin>>n>>m;vector<array<int,3>> a(n+1);vector<vector<pair<int,int>>> pos(n+2);for (int i=1;i<=n;i++) cin>>a[i][0];for (int i=1;i<=n;i++) cin>>a[i][1]>>a[i][2],sum+=a[i][2];for (int i=1;i<=n;i++) pos[a[i][0]].push_back({a[i][1],a[i][2]});vector<long long> dp(n+2,sum);dp[0]=0;for (int i=0;i<=n;i++){for (auto [b,v]:pos[i]) dp[i+m]=min(dp[i+m],dp[i]+b-v);dp[i+1]=min(dp[i],dp[i+1]);}cout<<sum+dp[n+1];return 0;
}
明显的动态规划问题,需要计算费用最小和:
\(\sum_{i=1}^nval_i\)考虑一头牛都不陪的情况,其中 \(sum(b_k-val_k)\) 表示陪第 \(k\) 头牛的精力值减去礼物值
前项的结果是一个给定的值,于是方程可以转化为:
对于后项,就可以考虑动态规划求解
for (int i=1;i<=n;i++) pos[a[i][0]].push_back({a[i][1],a[i][2]});
pos
数组记录第 a[i][0]
天,可以陪的牛的精力与礼物值
vector<long long> dp(n+2,sum);
dp[0]=0;//第0天不用陪牛,dp为0
for (int i=0;i<=n;i++){//递推天数for (auto [b,v]:pos[i]) //遍历这一天能陪的牛dp[i+m]=min(dp[i+m],dp[i]+b-v);//更新陪这头牛的费用和dp[i+1]=min(dp[i],dp[i+1]);//递推到第二天
}
状态转移的方向有两个:
- 第 \(i\) 天的费用和从第 \(i-m\) 天递推而来(第 \(i-m\) 天陪了一头牛)
- 第 \(i\) 天的费用和从第 \(i-1\) 天递推而来(第 \(i-m\) 天没有陪牛)
从第 \(0\) 天开始递推,那么第 \(i\) 天的状态就会转移到这天之后,换言之,之后天数的状态需要从这天转移过去