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\),所以是显然可以枚举每一个点的,然后我们对于这个公式进行一个转换。


切比夫雪距离

切比雪夫距离定义为

\[d((x_1,y_1),(x_2,y_2)) = \max(|x_1-x_2|,\ |y_1-y_2|) \]

令

\[u = x + y,\quad v = x - y \]

则

\[|u_1-u_2| = |(x_1+y_1)-(x_2+y_2)| = |(x_1-x_2)+(y_1-y_2)| \]

\[|v_1-v_2| = |(x_1-y_1)-(x_2-y_2)| = |(x_1-x_2)-(y_1-y_2)| \]

设 \(p = x_1-x_2\),\(q = y_1-y_2\),则

\[|u_1-u_2| + |v_1-v_2| = |p+q| + |p-q| \]

引理:对任意实数 \(p,q\),有

\[|p+q| + |p-q| = 2\max(|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|)\)。

因此

\[\max(|x_1-x_2|,\ |y_1-y_2|) = \frac{|u_1-u_2| + |v_1-v_2|}{2} \]

证毕。


转换完之后我们只需要用一个前缀和统计一下就做完了。

代码
#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;
}
posted @ 2026-09-22 07:49  tangkaiming  阅读(40)  评论(0)    收藏  举报