这几天比较忙可能组织不了视频直播讲解,希望同学们自己抽时间订正,有什么困难可以直接联系我
初赛的题目一定要去认真手算,用编译器做题不过是自欺欺人的把戏
A题题解
合法密码只能是数字+字母拼接分为两块组成
考虑到小写字母的ASCII码都是大于数字字符的
所以直接判断题目给定的串是不是非递减即可
#include <bits/stdc++.h>
using namespace std;
string solve() {
int n;
string s;
cin >> n >> s;
for(int i = 0; i + 1 < n; i ++) {
if(s[i] > s[i + 1]) return "NO";
}
return "YES";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T --)
cout << solve() << "\n";
}
B题题解
简单分类讨论题 ,A B数组只相差一个元素,所以操作可以分为两块
1: 把前N个元素变为一样
2:复制一个元素
显然如果第N+1号元素值能在操作1过程中产生的话,操作2就不需要额外修改
否则只能取操作1过程中出现的最接近 第N+1号元素的值再去做加减法
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
i64 solve() {
int n, x;
cin >> n;
vector<int> a(n), b(n);
for(int i = 0; i < n; i ++) cin >> a[i];
for(int i = 0; i < n; i ++) cin >> b[i];
cin >> x;
i64 ans = 1;
int mind = INT_MAX;
for(int i = 0; i < n; i ++) {
ans += abs(a[i] - b[i]);
if(x >= min(a[i], b[i]) && x <= max(a[i], b[i])) mind = 0;
else mind = min({mind, abs(x - a[i]), abs(x - b[i])});
}
return ans + mind;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T --)
cout << solve() << "\n";
}
C题题解
枚举第
i
i
i个人,那么他前面的人放哪个组是确定的,二分往后面查找什么位置会让编程组或者测试组放满,放满以后只能全部丢到另一个组去了
#include<bits/stdc++.h>
#define ll long long
#define pii pair<long long,long long>
#define pb push_back
#define eb emplace_back
#define debug(x) cerr<<x<<'\n';
using namespace std;
void solve()
{
int n,m;cin>>n>>m;
vector<int>a(n+2+m),b(n+2+m);
for(int i=1;i<=n+m+1;i++)
cin>>a[i];
for(int i=1;i<=n+m+1;i++)
cin>>b[i];
int N = n+m+1;
vector<ll>preb(N+1),prea(N+1),sb(N+1),sa(N+1),sufa(N+2),sufb(N+2);
for(int i=1;i<=N;i++){
sa[i]=sa[i-1]+(a[i]>b[i]?a[i]:0);
sb[i]=sb[i-1]+(b[i]>a[i]?b[i]:0);
prea[i]=prea[i-1]+(a[i]>b[i]);
preb[i]=preb[i-1]+(a[i]<b[i]);
}
for(int i=N;i;i--)
sufa[i]=sufa[i+1]+a[i],sufb[i]=sufb[i+1]+b[i];
int nown=0,nowm=0;ll sum=0;
for(int i=1;i<=N;i++) {
int l1=i,r1=N;
while(l1<r1) {
int mid=l1+r1>>1;
if(nown+prea[mid]-prea[i]>=n) r1=mid;
else l1=mid+1;
}
if(nown+prea[l1]-prea[i]<n) l1++;
int l2=i,r2=N;
while(l2<r2) {
int mid=l2+r2>>1;
if(nowm+preb[mid]-preb[i]>=m) r2=mid;
else l2=mid+1;
}
int l=min(l1,l2);
ll ans = sum+sa[l]-sa[i]+sb[l]-sb[i];
if(l1<l2) ans+=sufb[l+1];
else ans+=sufa[l+1];
cout<<ans<<' ';
if(a[i]>b[i]) {
if(nown<n) nown++,sum+=a[i];
else nowm++,sum+=b[i];
}else {
if(nowm<m) nowm++,sum+=b[i];
else nown++,sum+=a[i];
}
}
cout<<'\n';
}
main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cerr.tie(0);
int _ = 1;cin>>_;
while(_--) solve();
}
D题题解
简单讨论题,要么2个人互为好朋友,要么就是三元组
如果两个人互相是好友,那只用两人,否则三个人
#include<bits/stdc++.h>
using namespace std;
int A[55];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}
int ok=1;
for(int i=1;i<=n;i++){
if(A[A[i]]==i){
ok--;
break;
}
}
printf("%d\n",2+ok);
}
return 0;
}
E题题解
最优的方式其实是一个一个移动0
#include<bits/stdc++.h>
using namespace std;
char s[200005];
int main(){
int t;
scanf("%d",&t);
while(t--){
int cnt=0;
long long int ans=0;
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++){
if(s[i]=='1')cnt++;
else{
if(cnt!=0)ans=ans+cnt+1;
}
}
printf("%lld\n",ans);
}
return 0;
}
F题题解
考虑
d
p
dp
dp,对于前
i
i
i个数操作
0
到
k
0到k
0到k次得到的最优解
如果一段区间被操作了,那么最终的数组一定是这个区间中的最小值
操作的区间与区间之间不会有交叉部分,那么本题就基本上推导出来了,线性的
因为
k
k
k很小,每次可以暴力转移附近的数字
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<LL> a(n + 1), s(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
vector<vector<LL>> dp(n + 1, vector<LL>(k + 1, 1e16));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int x = 0; x <= k; x++) {
for (int l = i; l >= i - x && l >= 1; l--) {
int r = l + x;
if (r > n)
continue;
for (int t = 0; t <= k - x; t++) {
dp[r][t + x] = min(dp[r][t + x], dp[l - 1][t] + (a[i] * (x + 1)));
}
}
}
}
LL ans = 1e16;
for (int i = 0; i <= k; i++) {
ans = min(dp[n][i], ans);
}
cout << ans << endl;
}
return 0;
}
临时再加一道G题题解吧 ,感觉大家也是可以做的,只不过对于大家来说思维比较难
我们按照
b
i
bi
bi的值从大到小排序。枚举边界
i
i
i,从
1
到
i
1到i
1到i的部分选
k
k
k个,
i
+
1
到
n
i + 1 到 n
i+1到n的部分任意选择。
1
到
i
1到i
1到i部分选的
k
k
k个一定是这次选择里最大的
k
k
k个
b
b
b,这部分选的某个位置对答案的贡献是
−
a
i
-ai
−ai 负的 。将问题转化成选择
k
k
k个最小的
a
i
ai
ai,因此维护单调队列。
考虑到
i
+
1
到
n
i+1到n
i+1到n的部分任意选,由于最大的
k
k
k个
b
b
b已经选了,这部分选的某个位置对答案的贡献是
b
i
−
a
i
bi-ai
bi−ai 因为这部分是任意选的,为了让答案更大,对答案的贡献就是
b
i
−
a
i
bi-ai
bi−ai 结果为正值的和。
#include<bits/stdc++.h>
using namespace std;
struct pe{
int a;
int b;
}A[200005];
bool cmp(pe x,pe y){
// if(x.b==y.b)return (x.b-x.a >y.b-y.a);
return x.b<y.b;
}
// b-1 b b b b
// a a
priority_queue<int>q;
long long int sum[200005];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&A[i].a);
}
for(int i=1;i<=n;i++){
scanf("%d",&A[i].b);
}
sort(A+1,A+1+n,cmp);
for(int i=1;i<=n;i++){
if(A[i].a<A[i].b){
sum[i]=sum[i-1]+(A[i].b-A[i].a);
}
else{
sum[i]=sum[i-1];
}
}
if(k==0){
printf("%lld\n",sum[n]);
continue;
}
//按照B排序 枚举划分点
// 拿小于等于k 全部亏
// 拿大于K个 最大的K个Bi全部亏 剩下的全部拿 ai<bi的
// 在最大的B里面选择最小的K个ai 亏 剩下的算利润
// M个里面选K个 亏的最少的 () i (利润)
// K个
//4 K+1个丢掉最大的ai
long long int s=0;
long long int ans=0;//啥也不买 买啥亏啥
for(int i=n;i>=1;i--){
if(q.size()<k){
q.push(A[i].a);
s=s-A[i].a; //亏
continue;
}
else{
ans=max(ans,s+sum[i]);
q.push(A[i].a);
if(A[i].a!=q.top()){
s=s-A[i].a+q.top();
ans=max(ans,s+sum[i-1]);
}
else{
ans=max(ans,s+sum[i]);
}
q.pop();//扔掉当前最大的Ai 反正也是要亏 就少亏一点
}
}
printf("%lld\n",ans);
while(!q.empty())q.pop();
}
return 0;
}