为了能到远方,脚下的每一步都不能少.|

园龄:粉丝:关注:

Hello 2025 A-D

题解

博主能力有限

A

题意:将0~(n*m-1)的排列,任意放在n×m的二维数组,找到一种放置后的,每行每列没有出现过的最小整数的和,不关心怎么放置,只关心怎么求最大的和
理解对题意后,很容易发现只有0所在的行和列才能使ans增加,而最大的增加方法,假设m>n,那么与0的同一行设置为0-(m-1)的排列,ans+=m,与0的同 一列,那必然是ans+=1,当m<n时,同理
所以最终答案:
ans=max(n,m)+1

B

题意

一个长度为a的数组
给出一次操作:任选区间并删除区间内的所有最小值
操作之前有k次变换,将数组内的数任意变换
求最少操作次数使得数组为空

思路

为了尽可能减少操作次数,那么一次操作就要多删除相同的最小值,所以每次都选择全部的区间
这样选择后,我们的操作次数就是,数组内有多少个不等的数
而操作前的k次变换,可以减少不等的数,进一步使得答案减少
所以根据每个数相同的有多少个进行从小到大排序,从小到大依次减少不等的数,直到k用完

CODE

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
int n,k;
int t;
int top=0;
const int maxn=1e5+10;
int a[maxn];
map<int,int>mp;

void solve(){
	cin>>n>>k;
	mp.clear();
	for(int i=1;i<=n;++i){
	    int x;
		cin>>x;
		mp[x]+=1;
	}
	if(n==1 || k>=n-1){
		cout<<1<<endl;
		return ;
	}
	int maxx=0;top=0;
    for(auto[x,y]:mp){
        a[++top]=y;
    }
    sort(a+1,a+1+top);
	int ans=0;
	bool flag=0;
	for(int i=1;i<=top;++i){
	    if(k>=a[i]){
	        k-=a[i];
	    }
	    else {
	        flag=1;    
	    }
	    if(flag==1){
	        ans+=1;
	    }
	}

	cout<<ans<<endl;
	return ;
}
int main(){
	cin>>t;
	while(t--){
		solve();
	}return 0;
}

C

题意

给定区间[l,r],找三个数使得ab+bc+ac最大,且abc

思考(官方)

当时看到异或就被吓退了,题没有想象那么难

我们考虑第k个二进制位,如果在该位置上a,b,c三位中任取1或2个数为0,那么对答案贡献2k+1,如果k是l,r最大不同的二进制位,所以答案最多是2(1+2++2k)(可以思考一下为什么这样设置k的原因)

剩下的问题就是如何构造的问题了
官方思路:
找到第一个l和r不同的二进制位设为k,令a为前k-1个二进制位都为1,令b为第k位为1,其它0,这时再这样在区间任取一个不等于a,b的就可以

再讲细点:
a,b已经保证了前k位都不同,这时任取一个区间内的数(不等于a,b),能保证c与不可能在第i个二进制位上都等于a,b,所以必然贡献2i+1

CODE

#include<bits/stdc++.h>

using namespace std;
int t;
void solve(){
	int l,r;cin>>l>>r;
	int k=31-__builtin_clz(l^r);
	int a=l|(1<<k)-1,b=a+1,c=a==l?r:l;
	cout<<a<<" "<<b<<" "<<c<<endl; 
}
int main(){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

D

向这位博主学习的

思路

我们转化一下题目的条件,那么当区间[l,r]的端点,为区间的最大最小值,如果ar是最大值,al是最小值,ans=arr(all),
如果al是最大值,ar是最小值,all(ar+r),
综上:
ans=max(arr(all),all(ar+r))

修改的操作很符合线段树单点修改的特性,可以用线段树来维护

CODE

#include<bits/stdc++.h>

using namespace std;
#define ls p<<1
#define rs p<<1|1 
int n;
const int maxn=8e5+10;
int t,q,x,p;
int m[maxn]; 
int mina[maxn],minb[maxn];
int maxa[maxn],maxb[maxn]; 
int ansa[maxn],ansb[maxn];
void push_up(int p){//对于一个节点的最大的ans应该从左儿子的ans和右儿子的ans,左右区间中最大值最小值按照公式相减得到
	mina[p]=min(mina[ls],mina[rs]);maxa[p]=max(maxa[ls],maxa[rs]);
	minb[p]=min(minb[ls],minb[rs]);maxb[p]=max(maxb[ls],maxb[rs]);
	ansa[p]=max(max(ansa[ls],ansa[rs]),maxa[rs]-mina[ls]);
	ansb[p]=max(max(ansb[ls],ansb[rs]),maxb[ls]-minb[rs]);
	return ;
}
void build(int p,int l,int r){
	if(l==r){
		mina[p]=maxa[p]=m[l]-l;
		minb[p]=maxb[p]=m[l]+l;
		ansa[p]=ansb[p]=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	push_up(p);
}

void update(int p,int l,int r,int po,int ua,int ub){//单点更新
	if(l==r){
		mina[p]=maxa[p]=ua;
		minb[p]=maxb[p]=ub;
		ansa[p]=ansb[p]=0;return ;
	}
	int mid=(l+r)>>1;
	if(po<=mid) update(ls,l,mid,po,ua,ub);
	else update(rs,mid+1,r,po,ua,ub);
	push_up(p);
}
void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;++i) cin>>m[i];
	build(1,1,n);
	cout<<max(ansa[1],ansb[1])<<"\n"; 
	while(q--){
		cin>>p>>x;
		update(1,1,n,p,x-p,p+x);
		cout<<max(ansa[1],ansb[1])<<"\n";	
	}return ;
}
int main(){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

本文作者:归游

本文链接:https://www.cnblogs.com/guiyou/p/18666921

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   归游  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起