逆序对——P3054 [USACO12OPEN]跑圈Running Laps

题目描述

农夫约翰让他的 n (1 <= n <= 100,000) 头牛在长度为 c 的跑道上进行跑 l 圈的比赛,所有牛从同一起点,以不同的速度开始跑。直到当跑得最快的那一头牛跑完 l 圈时,所有牛才同时停下。

约翰发现在跑圈过程中发生了几次“超越事件”。其定义是:在比赛结束前某时刻,奶牛 x 已经超越了奶牛 y 整整一圈,则称做一次“超越事件”。(注: 至少一圈 ,超越了1/2圈,或者超越了1/4圈等等都不算。且对于同一对奶牛(x,y)不会重复计算次数。)

约翰想知道比赛过程中发生了多少次“超越事件”。

(注:可能原文章表达有误或某些其他原因,各种翻译方式过来的题意都有问题,给人误导很大,这里是根据题目数据和样例解释写的正确的题意,而不是原文)

输入格式:

第一行:三个整数:n,l,c(1 <= l,c <= 25,000)

第二行到第 n+1 行:每行一个整数 vi ,表示奶牛i的速度(1 <= vi <= 1,000,000)

输出格式:

第一行:一个整数,表示总共发生的“超越事件”的次数

证明

t = L * c / v_max

vi * t - vj * t >= c * x // x 为套圈次数
n = (vi - vj) * L / v_max (下取整)
n = (vi * L / v_max) - (vj * L / v_max ) (条件:(vi*L % v_max) >= (vj*L % v_max))
在整体上求出 L * vi % v_max 的逆序对数减去

struct Node{
    long long dat, nam;
}b[100001];
long long N, L, C, v[100001], max_v, tr[100001];
long long new_v[100001], sum[100001], ans, nxd;

bool read(long long &x)
{
    x = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') {
        x = x*10 + ch - '0';
        ch = getchar();
    }
    return x;
}
bool cmp(long long a, long long b) { return a < b; }
bool cmp2(long long a, long long b) { return a < b; }
bool cmp3(Node a, Node b){ 
    if (a.dat > b.dat) return true;
    if (a.dat == b.dat && a.nam > b.nam) return true;
    return false;
}

long long sum_x(long long x)
{
    long long re = 0;
    while (x){
        re += tr[x];
        x -= (x&-x);
    }
    return re;
}

void add(long long x, long long val)
{
    while (x<=N){
        tr[x] += val;
        x += (x&-x);
    }
}
int main()
{
    read(N); read(L); read(C);
    for (long long i=1; i<=N; i++){
        read(v[i]);
        max_v = max(max_v, v[i]);
    }
    sort(v+1,v+N+1,cmp2);
    for (long long i=1; i<=N; i++){
        new_v[i] = (long long) ((L * v[i]) / max_v);
        sum[i] = sum[i-1] + new_v[i];
    }
    for (long long i=N; i; i--){
        ans = ans + (i-1)*new_v[i] - sum[i-1];  
    }

    for (long long i=1; i<=N; i++){
        b[i].dat = ((v[i]*L) % max_v);
        b[i].nam = i;
    }             

    sort(b+1, b+N+1, cmp3);
    for (long long i=1; i<=N; i++){
        long long pos = b[i].nam;
        nxd += sum_x(pos-1);
        add(pos, 1);
    }
    cout<<ans-nxd<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值