USACO2011Open Silver Running Laps题解

//请忽略我把牛看作羊....

由题意,我们可以得到 t*vi-t*vj=kC.那么i,j相遇的次数就是最大的k(整数).为了得到最大的k就要使t最大,而tmax=L*C/vmax.

那么 把式子整理得到:

     k=L*(vi-vj)/vmax.(向下取整)

     对于第i只,它和速度比它小的每一只羊相遇的次数都能确定,

常见的思路就是运用前缀和把式子累加,但由于k是每两只羊之间运算取整的结果,加到一起后,答案会发生变化.所以不能单单相加运算,我们可以算出累加算法对正确答案产生的影响.

     先看两个式子:

A1=[a*t+b-(c*t+d)]/t 与A2= (a*t+b)/t-(c*t+d)/t(a,c为整数;b,d∈[0,t-1])

     变形得:

A1=[(a-c)*t+b-d]/t

A2=a-c.

只有当b-d<0,即b<d时A1!=A2且A1=A2-1. 

     现在我们来看原题:

     当L*C*(vi-vj)/(vmax*C)变为

( L*C*vi)/(vmax*C)- ( L*C*vi)/(vmax*C)时,答案会不会改变取决于L*C*vi和L*C*vj除vmax*C的余数大小关系.

如果前者小于后者,答案要减去1.

我们按照速度从大到小枚举每一只羊i

对于羊i的答案 =(i-1)*L*vi/vmax-L*sumv[i-1]/vmax-cnt.

此处cnt表示余数大于羊i的余数,且速度小于羊i的个数.

我们将余数x排序.再用树状数组维护v<vi,且x>xi的个数就可以啦.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
const int M=1e5+5;
const int N=1e6+5;
int n,L,vmx,C;
int val[M],tot=0;
struct node{
    int v,p;
    ll mod;
}A[M];
void add(int x){//树状数组更新 
    while(x<=tot){
        val[x]++;
        x+=x&(-x);
    }
}
int sum(int x){//树状数组求和 
    int res=0;
    while(x>0){
        res+=val[x];
        x-=x&(-x);
    }
    return res;
}
bool cmp1(node a,node b){ return a.v<b.v; }//按照速度排序 
bool cmp2(node a,node b){ return a.mod<b.mod; }// 按照余数排序 
int main(){
    int i,j;
    scanf("%d %d %d",&n,&L,&C);
    for(i=1;i<=n;i++){
        scanf("%d",&A[i].v);
        vmx=max(vmx,A[i].v);//z找到最大的速度 
    }
for(i=1;i<=n;i++)A[i].mod=1ll*L*C*A[i].v%(1ll*C*vmx);//
    sort(A+1,A+1+n,cmp2);
    for(i=1;i<=n;i++){
        tot++;
        for(j=i;j<=n;j++){
            if(A[j].mod!=A[i].mod)break;
            A[j].p=tot;
        }
        i=j-1;
    }
    sort(A+1,A+1+n,cmp1);
    ll sum_pre=0,res=0;
    for(i=1;i<=n;i++){
        ll tmp=1ll*L*A[i].v/vmx; 
        ll ans=tmp*(i-1)-sum_pre+sum(A[i].p)-i+1;
        sum_pre+=tmp;
        add(A[i].p);
        res+=ans;
    }
    cout<<res<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值