题解
博主能力有限
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],找三个数使得\(a \oplus b + b \oplus c + a \oplus c\)最大,且\(a\neq b\neq c\)
思考(官方)
当时看到异或就被吓退了,题没有想象那么难
我们考虑第k个二进制位,如果在该位置上\(a,b,c\)三位中任取1或2个数为0,那么对答案贡献\(2^{k+1}\),如果k是l,r最大不同的二进制位,所以答案最多是\(2*(1+2+\cdots+2^{k})\)(可以思考一下为什么这样设置k的原因)
剩下的问题就是如何构造的问题了
官方思路:
找到第一个l和r不同的二进制位设为k,令a为前k-1个二进制位都为1,令b为第k位为1,其它0,这时再这样在区间任取一个不等于a,b的就可以
再讲细点:
a,b已经保证了前k位都不同,这时任取一个区间内的数(不等于a,b),能保证c与不可能在第i个二进制位上都等于a,b,所以必然贡献\(2^{i+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]\)的端点,为区间的最大最小值,如果\(a_r\)是最大值,\(a_l\)是最小值,\(ans=a_r-r-(a_l-l)\),
如果\(a_l\)是最大值,\(a_r\)是最小值,\(a_l-l-(a_r+r)\),
综上:
\(ans=max(a_r-r-(a_l-l),a_l-l-(a_r+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;
}