题目大意:n个球,编号0-n-1,一开始有a个盒子,将编号x的球放到编号x%a的盒子里,现在又来了b个盒子,重新放小球,将编号x的球放到编号x%b的盒子里。记代价为abs(x%a - x%b),求总代价。
题目分析:公式很简单:cost = sigma(abs(i%a - i%b))。明显以lcm(a,b)为循环节,但是lcm(a,b)也会非常大,直接做会T,看公式。
考虑到公式里面有绝对值,想办法去掉绝对值,于是我们只需要找到某个连续的区间使i%a>i%b,剩下的区间就是i%a<i%b,对于一个连续的区间求i%a或者i%b是直接可以算的。而区间也不难找。拿笔在纸上画画就ok。
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<string>
using namespace std;
const int N = 100005;
const int M = 10000005;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const double PI = acos(-1.0);
typedef __int64 ll;
ll n,m;
ll gcd(ll a,ll b)
{
while(b)
{
ll t=a%b;
a=b;
b=t;
}
return a;
}
ll lcm(ll a,ll b)
{
return a/gcd(a,b)*b;
}
int main()
{
ll i,j;
ll t;
ll Lcm;
ll p;
scanf("%I64d",&t);
while(t --)
{
scanf("%I64d",&p);
scanf("%I64d%I64d",&n,&m);
if(n == m)
{
printf("0\n");
continue;
}
if(n < m)
swap(n,m);
Lcm = lcm(n,m);
j = Lcm/n;
ll t1,t2;
ll l1,l2;
ll sum1 = 0;
ll sum2 = 0;
ll sum = 0;
ll sum3,sum4;
if(p >= Lcm)//循环节部分
{
for(i = 1;i < j;i ++)
{
t1 = n * i;
t2 = t1 / m;t2 += 1;
l1 = t2 * m - t1;
l2 = m - 1 - l1 + 1;
sum1 += (l2 + m - 1)*l1/2;
sum2 += (l1 - 1)*l1/2;
}
sum3 = (m - 1)*m/2;
sum3 *= Lcm;
sum3 /= m;
sum4 = (n - 1) * n/2;
sum4 *= Lcm;
sum4 /= n;
sum3 -= sum1;
sum4 -= sum2;
sum = sum1 - sum2 + sum4 - sum3;
}
ll ans = (p/Lcm) * sum;
ll len = p%Lcm;//尾巴
ll tans = 0;
j = len/n;
for(i = n*j;i < len;i ++)//尾巴的尾巴,直接暴力
{
if(i%n - i%m > 0)
tans += (i%n - i%m);
else
tans += (i%m - i%n);
}
sum1 = sum2 = 0;
for(i = 1;i < j;i ++)
{
t1 = n * i;
t2 = t1 / m;t2 += 1;
l1 = t2 * m - t1;
l2 = m - 1 - l1 + 1;
sum1 += (l2 + m - 1)*l1/2;
sum2 += (l1 - 1)*l1/2;
}
sum3 = (m - 1)*m/2;
sum3 *= (j * n / m);
ll s = (j*n/m)*m;
for(i = s;i < j*n;i ++)
sum3 += (i%m);
sum3 -= sum1;
sum4 = (n - 1)*n/2;
sum4 *= j;
sum4 -= sum2;
tans += (sum1 - sum2 + sum4 - sum3);
ans += tans;
printf("%I64d\n",ans);
}
return 0;
}
//78MS 248K