牛客练习赛113 D 小红的数组操作(hard version)

发布时间 2023-07-08 20:52:08作者: 突破铁皮

题目要求求出最小的总代价使得平均数为整数,转换式子可得实际就是求出a,b使得(a*x-b*y+sum)%n==0且a*p+b*q要最小,平均值的为sum/n,因此对sum进行操作使其成为n的倍数即可

(a*x-b*y+sum)%n==0

=>((a*x+sum)%n-b*y%n)%n==0

因为(a*x+sum)%n<n,b*y%n<n,因此要想二者差求余数为0一定为(a*x+sum)%n=b*y%n,又因为对于任意倍数的x来说对n取余的余数个数最多不超过n

因此我们先预处理求得所有(a*x+sum)%n的最小花费然后再遍历b*y%n判断是否有对应的a求所有的最小花费

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <set>
#include <utility>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll n,p,x,q,y;
map<ll,ll> f;
void solve(){
    ll sum=0;
    cin>>n>>p>>x>>q>>y;
    for(int i=1;i<=n;i++){
        int u;
        cin>>u;
        sum+=u;
    }
    for(int i=0;i<n;i++){
    	if(!f[(i*x%n+sum%n)%n])//注意求最小花费,因为由小到大枚举因此第一次枚举到的一定为最小的
        f[(i*x%n+sum%n)%n]=(i+1)*p;//加一是为了让所有对应的花费都大于0好判断
    }
    ll res=1e18;
    for(int i=0;i<=n;i++){
        int u=i*y%n;
        if(f[u]>0){
            res=min(res,f[u]+i*q-p);//这里要对应的减去1个p
        }
    }
    if(res==1e18) cout<<-1;
    else cout<<res;
    
}
int main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}