Description
小包有N个球和A个盒子。球标号为0到N-1,盒子编号为0到A-1。标号为x的球放在下标为x mod A的盒子里。
然后他得到了B个新盒子,编号为0到B-1。他想把所有球从旧盒子里面拿出来放到新盒子,标号为x的球放在下标为x mod B的盒子里。
对于某个球来说,如果原本放在下标为a的旧盒子,现在放在下表为b的新盒子,那么费用为|a-b|。
求出总花费。
Input
输入3个整数N,A和B,同一行。
Output
输出总花费。
Sample Input
输入1:
1000000000 1 1
输入2:
8 2 4
Sample Output
输出1:
0
输出2:
8
Data Constraint
对于30%的数据有1≤N≤20
对于全部数据,有1≤N≤1000000000,A,B≤100000
Solution
我们先将n变成a的倍数,剩下不足a个暴力。
我们根据循环节先找出所有%a=0的数,记s[x]为这些数中%b=x的个数,t[x]为s[x]*x,然后我们枚举%a的余数k,可以发现所有%a=k的数的数量是等于%a=0的数的数量的,并且,我们可以将这些数看作%a=0的每个数+k得来。接着,对于这些数中原先%b=x的数,它的新的余数就变成了(x+k)%b,可以将b数组看成整体向右移了k位,超过k的变成1,因此,对于k我们可以用%a=0的转移过来。具体只要分类讨论| i%a - i%b |的绝对值内数的正负性。
1.若是正的,即k>i%b,i%b取0~k-1中的数,那么我们要取的数即为(x+k)%b的范围在0~k-1中的所有x,然后这些数的个数*k-sum(i%b)就是答案。
2.若是负数,即k<i%b,i%b取k+1~b-1中的数,类似求答案。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define ll long long
using namespace std;
int n,a,b;
ll ans,sum,s[N],t[N];
int main(){
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
scanf("%d%d%d",&n,&a,&b);
if(a>b) swap(a,b);
for(int i=(n/a)*a;i<=n-1;i++) sum+=abs(i%a-i%b);
n=(n/a)*a;
for(int i=0;i<=n/a-1;i++){
int r=(i*a)%b;
if(!s[r]) s[r]++;
else{
int k=(n/a)/i;
for(int j=0;j<=b-1;j++) s[j]*=k;
for(int j=i*k+1;j<n/a;j++) s[(j*a)%b]++;
break;
}
}
t[0]=s[0];
for(int i=1;i<=b-1;i++){
t[i]=t[i-1]+s[i]*i;s[i]+=s[i-1];
}
for(int k=0;k<=a-1;k++){
ans+=k*(s[b-1]-s[b-k-1])-(t[b-1]-t[b-k-1])+(b-k)*(s[b-1]-s[b-k-1]);
ans+=t[b-k-1]-t[0]-k*(s[b-k-1]-s[0])+k*(s[b-k-1]-s[0]);
}
printf("%lld\n",ans+sum);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/94410772