ABC476考试总结
AB 比较简单,不讲。
C
我们使用一个数组 \(maxn\) 记录当前最大的前三个数,按从大到小的顺序排列,分三种情况考虑。(假设当前新加入的值是 \(a\)。)
- \(a \ge maxn_1\):将 \(maxn_2\) 赋给 \(maxn_3\),\(maxn_1\) 赋给 \(maxn_2\),最后把 \(a\) 赋给 \(maxn_1\)。这样保留原先第 \(1,2\) 大的数和新加入的 \(a\)。
- \(a \ge maxn_2\):将 \(maxn_2\) 赋给 \(maxn_3\),然后把 \(a\) 赋给 \(maxn_2\)。这样保留原先第 \(1\) 大的数和新加入的 \(a\)。
- \(a \ge maxn_3\):将 \(a\) 赋给 \(maxn_3\)。这样保留原先第 \(1,2\) 大的数和新加入的 \(a\)。
三种情况只会满足一种,否则相当于加入了多个 \(a\)。
code
#include<bits/stdc++.h>
using namespace std;
int a[500005];
int maxn[5];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>=maxn[1]){
maxn[3]=maxn[2];maxn[2]=maxn[1];
maxn[1]=a[i];
}else if(a[i]>=maxn[2]){
maxn[3]=maxn[2];
maxn[2]=a[i];
}else if(a[i]>=maxn[3])maxn[3]=a[i];
if(i>2)cout<<maxn[3]<<"\n";
}
return 0;
}
D
由于 \(n \le 2 \times 10^5\),时间复杂度应为 \(O(n)\) 或 \(O(n \log n)\)。
显然,应从便宜的饮料开始购买,所以我们分别对 \(a,b\) 数组从小到大枚举。
由于饮料贩卖机只能接受 \(k\) 元的货币,我们考虑枚举在饮料贩卖机购买多少瓶饮料,从而计算出剩余的钱数。接下来需要在 \(O(\log n)\) 的时间内计算可以在甜点自动贩卖机购买多少份甜点。
我们对数组 \(a\) 做前缀和,然后在前缀和上二分即可。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,m,k;
int x,y;
int a[N],b[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>k>>x>>y;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i];
sort(a+1,a+n+1);sort(b+1,b+m+1);
for(int i=1;i<=n;i++)a[i]+=a[i-1];
int ans=upper_bound(a+1,a+n+1,y*k+x)-a-1;//不买饮料
for(int i=1;i<=m;i++){
int cnt=(b[i]+k-1)/k;
if(cnt>y)break;
x+=cnt*k-b[i];y-=cnt;
int now=i+upper_bound(a+1,a+n+1,y*k+x)-a-1;
ans=max(ans,now);
}
cout<<ans;
return 0;
}
E
可以直接按照题意模拟,求出最小值和最大值的位置,然后交换。
求位置的过程可以用线段树优化,交换后再修改线段树即可。
这里详细讲一下寻找位置的过程:如果当前的 \([l,r]\) 完全在查询区间之内,则判断最小值在左子树还是右子树,然后递归寻找。
如果不完全在查询区间内,则判断左右子树是否与查询区间有交集,若有则递归寻找,最后取两者返回值中位置较小的那个。
代码
#include<bits/stdc++.h>
#define int long long
#define ls id<<1
#define rs ls|1
using namespace std;
const int N=200005;
int n,m;
int a[N];
int minn[N<<2],maxn[N<<2];
void pushup(int id){
minn[id]=min(minn[ls],minn[rs]);
maxn[id]=max(maxn[ls],maxn[rs]);
return;
}
void build(int l,int r,int id){
if(l==r){
minn[id]=maxn[id]=a[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
pushup(id);
return;
}
void update(int l,int r,int id,int x){
if(l==r){
minn[id]=maxn[id]=a[l];
return;
}
int mid=(l+r)>>1;
if(x<=mid)update(l,mid,ls,x);
if(mid<x)update(mid+1,r,rs,x);
pushup(id);
return;
}
int query_minn(int l,int r,int id,int x,int y){
if(l==r)return l;
int mid=(l+r)>>1;
if(x<=l && r<=y){//在查询区间内部
if(minn[id]==minn[ls])return query_minn(l,mid,ls,x,y);//在左子树
return query_minn(mid+1,r,rs,x,y);//在右子树
}
int pos=0;
if(x<=mid)pos=query_minn(l,mid,ls,x,y);//左边可以
if(mid<y){//右边可以
int val=query_minn(mid+1,r,rs,x,y);
if(!pos || a[val]<a[pos])pos=val;//取最小值
}
return pos;
}
int query_maxn(int l,int r,int id,int x,int y){//同上
if(l==r)return l;
int mid=(l+r)>>1;
if(x<=l && r<=y){
if(maxn[id]==maxn[ls])return query_maxn(l,mid,ls,x,y);
return query_maxn(mid+1,r,rs,x,y);
}
int pos=0;
if(x<=mid)pos=query_maxn(l,mid,ls,x,y);
if(mid<y){
int val=query_maxn(mid+1,r,rs,x,y);
if(!pos || a[val]>a[pos])pos=val;
}
return pos;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,n,1);
while(m--){
int l,r;cin>>l>>r;
if(l==r)continue;
int x=query_minn(1,n,1,l,r),y=query_maxn(1,n,1,l,r);
swap(a[x],a[y]);
update(1,n,1,x);update(1,n,1,y);
}
for(int i=1;i<=n;i++)cout<<a[i]<<' ';
return 0;
}
F
题意
有一个 \(N\) 行 \(N\) 列的网格城市,每个格子 \((i,j)\) 有一家咖啡馆。
参与者人数分布:格子 \((i,j)\) 中居住的人数为 \((A_i \times B_j) \bmod M\)。
一个人从格子 \((s_r, s_c)\) 前往格子 \((t_r, t_c)\) 的旅行费用为 \(\max(|s_r - t_r|, |s_c - t_c|)\)(切比雪夫距离)。
定义 \(f(i,j)\) 为所有人到格子 \((i,j)\) 的咖啡馆集合时的总旅行费用。
对于每个格子 \((i,j)\),求出 \(f(i,j) + (i-1)N + (j-1)\),然后输出所有格子该值的按位异或和。
题解
因为 \(n<=1500\),所以是显然可以枚举每一个点的,然后我们对于这个公式进行一个转换。
切比夫雪距离
切比雪夫距离定义为
令
则
设 \(p = x_1-x_2\),\(q = y_1-y_2\),则
引理:对任意实数 \(p,q\),有
引理证明:分三种情况讨论即可
- 若 \(p \ge q \ge 0\):则 \(|p+q| = p+q\),\(|p-q| = p-q\),相加得 \(2p = 2\max(p,q)\)。
- 若 \(q \ge p \ge 0\):同理得 \(2q = 2\max(p,q)\)。
- 若 \(p,q\) 异号:设 \(p \ge 0 \ge q\),则 \(|p+q| = |p-|q||\),\(|p-q| = p+|q|\),相加得 \(2\max(p,|q|)\)。
因此
证毕。
转换完之后我们只需要用一个前缀和统计一下就做完了。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1505;
int n,m;
int a[N],b[N];
int x[N<<1],y[N<<1];
int cnt1[N<<1],sum1[N<<1];
int cnt2[N<<1],sum2[N<<1];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
int pyl=n+1,maxn=2*n+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int val=(a[i]*b[j])%m;
x[i+j]+=val;y[i-j+pyl]+=val;
}
}
for(int i=2;i<=maxn;i++){
cnt1[i]=cnt1[i-1];sum1[i]=sum1[i-1];
if(i<=2*n)cnt1[i]+=x[i],sum1[i]+=i*x[i];
}
for(int i=2;i<=maxn;i++){
cnt2[i]=cnt2[i-1];sum2[i]=sum2[i-1];
if(i<=2*n)cnt2[i]+=y[i],sum2[i]+=(i-pyl)*y[i];
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int s=i+j,t=i-j+pyl;
int val1=2ll*s*cnt1[s]-2ll*sum1[s]+sum1[maxn]-s*cnt1[maxn];
int val2=2ll*(t-pyl)*cnt2[t]-2ll*sum2[t]+sum2[maxn]-(t-pyl)*cnt2[maxn];
int val=(val1+val2)/2+(i-1)*n+(j-1);
ans^=val;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号