逆序对——P3054 [USACO12OPEN]跑圈Running Laps
程序员文章站
2022-03-03 20:13:01
...
题目描述
农夫约翰让他的 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;
}
上一篇: 两种方法对经典最小二乘法的改进