EF Java题解,代码已去除冗余~~~
构造矩形的时候,分两种情况,要么以x轴为长边,要么以x轴为宽边;对于情况一(假设当前位置的横坐标为a[i]
),需要的是使得a[i]-a[j]>=k 且 a[i]-a[j]-k<=m
的所有前置边a[j]
,而对于情况二,需要的是使得a[i]-a[j]+k<=m
的所有前置边a[j]
,分别维护前缀和以及有序映射即可快速求得答案,时间复杂度O(nlogn)
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt(),k=sc.nextInt();
TreeMap<Integer,Integer> map=new TreeMap<>();
long ans=0,pre[]=new long[n+5];
for(int i=1;i<=n;i++){
int a=sc.nextInt();
//x轴为底边:
Integer l=map.higherKey(a-k-m-1),r=map.lowerKey(a-k);
if(l!=null&&r!=null){
l=map.get(l);
r=map.get(r);
ans+=pre[r]-pre[l-1]+(long)(r-l+1)*(m-a+k+1);
}
//y轴为底边:
l=map.higherKey(a+k-m-1);
if(l!=null){
l=map.get(l);
ans+=pre[i-1]-pre[l-1]+(long)(i-l)*(m-a-k+1);
}
map.put(a,i);
pre[i]=pre[i-1]+a;
}
System.out.println(ans);
}
}
题目中所述的操作等同于,每次选择n-k个数增加1,求最小的操作次数,需要找到最小的可以使得数组sum整除的n的最少次数p∈[0,n-1]
,之后次数需要在p的基础上增加gcd(n,n-k)
的最小非负整数倍,使得数组平均值不小于数组最大值,且对于数组最小值的增加次数不能大于操作次数,二分即可,事假复杂度O(n+logC),C==6e18
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),k=n-sc.nextInt(),max=0,min=(int)2e9;
long sum=0;
for(int i=0;i<n;i++){
int a=sc.nextInt();
sum+=a;
max=Math.max(max,a);
min=Math.min(min,a);
}
if(k==0){
System.out.println(max==min?0:-1);
}
else{
//先找到最少加k的次数,使得sum%n==0
int p=0;
while(p<n&&(sum+(long)p*k)%n!=0){
p++;
}
if(p==n){
System.out.println(-1);
}
else{
long inc=n/gcd(k,n),l=0,r=(long)(8e18/inc/k);
//增加的次数可以表示为num=p+inc*w,w∈N
//(sum+k*num)/n>=max 且 (sum+k*num)/n-num<=min
while(l<r){
long mid=(l+r)>>1;
if(check(max,min,sum,p+inc*mid,k,n)){
r=mid;
}
else{
l=mid+1;
}
if(l==r-1){
if(check(max,min,sum,p+inc*l,k,n)){
r=l;
}
break;
}
}
System.out.println(p+inc*r);
}
}
}
static boolean check(long max,long min,long sum,long num,long k,long n){
return (sum+k*num)/(double)n>=max&&(sum+k*num)/(double)n<=min+num;
}
static long gcd(long a,long b){
return a<b?gcd(b,a):b==0?a:gcd(a%b,b);
}
}