农夫约翰最喜欢的操作
题目链接:https://www.acwing.com/problem/content/description/6134/
题意:
给定一个长度为n的数组a,一个模数m。每次可以对数组的一个元素+1或-1,让你求对某个x,对数组元素操作后,使(ai-x)%m==0 的最小操作数
思路:
使(ai-x)%m=0,即ai%m=x%m,不妨令x<m => ai%m=x
问题转化为:加减ai 使得ai mod m 等于相同的数字,由此可以想到中位数定理(即一个序列数字都转化为其中位数时,花费代价最小)
然而由于模数的运算 譬如 ai=8 ,m=9 ai%m=8 要想令其模数等于3 可以减5 也可以加4
发现 模数序列是在 一个环 上作运算的,(即环头为a1,环尾为an)(拆环成链:a[1] a[2] ···a[n] a[1] a[2] ···a[n] i~i+n-1 为一个环),那么在 环上 的每个数 都可以作为中位数 **
枚举i:1~n ,作为开头 那么p=i+n/2即中点**,分别求 左右两端 到 中点的距离,(这部分用前缀和实现)
注意的是 a[n+1] 到 a[n] 的距离 应该为 a[n+1]-a[n] +m,所以让拼接后片段都加上m
这样是正确的,因为环上的最短距离不会是绕远的
#include<bits/stdc++.h> #define rep(i,a,n) for(int i=a;i<=n;i++) #define pb push_back #define endl "\n" #define int long long #pragma GCC optimize(3) using namespace std; typedef long long ll; typedef pair<int,int> pii; const int inf=0x3f3f3f3f; const ll llmax=LLONG_MAX; const int maxn=2e5+5; const int mod=1e9+7; int n,m; int a[maxn]; int res[maxn]; int k[maxn*2]; int ans; int pre[maxn*2]; signed main() { ios::sync_with_stdio(false),cin.tie(0); int t;cin>>t; while(t--){ ans=llmax; cin>>n>>m; rep(i,1,n) cin>>a[i]; rep(i,1,n) { res[i]=a[i]%m; } sort(res+1,res+1+n); rep(i,1,n){ k[i]=res[i]; } rep(i,n+1,2*n){ k[i]=res[i-n]+m; } rep(i,1,2*n){ pre[i]=pre[i-1]+k[i]; } rep(i,1,n){ int p=i+n/2; int cnt=0; cnt+=(p-i)*k[p]-(pre[p-1]-pre[i-1]); cnt+=(pre[i+n-1]-pre[p])-(i+n-1-p)*k[p]; ans=min(ans,cnt); } cout<<ans<<endl; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现