hdu4611 Balls Rearrangement

题目请戳这里

题目大意: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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值