ZJNU 2479 - 车辆探测器 (差分数组/树状数组)

浙江师范大学第十八届大学生程序设计竞赛(重现) H - 车辆探测器

ZJNU 2479 - 车辆探测器


题意

\(n\)辆汽车在\(x\)轴负半轴排成一列以每秒\(1\)米的速度匀速向\(x\)正半轴行驶,第\(i\)辆汽车的初始位置为\(a[i]\)。

有\(m\)个车辆探测器,探测范围为\(\pm r[i]\)的探测器被放在\(x\)正半轴\((r[i],0)\)的位置。

只要每个车辆探测器的探测范围内有车存在,那么每秒会消耗\(P\)焦耳的能量。

如果原本探测范围内没有车存在,车辆探测器会关闭,一旦有车进入探测范围,探测器每次打开需要消耗\(C\)焦耳的能量。

对于每个探测器,分别求出它们消耗的能量。

限制

\(1\leq n\leq 10^5\)

\(1\leq m\leq 10^5\)

\(1\leq P,C\leq 10^6\)

\(-2\times 10^6\le a[i]\lt a[i+1]\lt 0\)

\(1\le r[i]\le 10^8\)




思路

一下子一年就过去了呢,这题去年正式比赛时没有过多少人,借这次重现赛重新写一遍整理思路

当时应该是树状数组过的,现在想了下好像差分数组直接就可以做,还更方便

(一年过去了思维还是没有什么质的飞跃呢……)


换句话说,对于每个探测范围为\(\pm r\)的探测器,其探测的左右最大距离即为\(2r\)

所以只要有两辆车的间隔\(\gt 2r\),那么探测器就会多消耗\(C\)焦耳的能量


假设探测器从第\(1\)辆车进入探测范围开始,到第\(n\)辆车离开探测范围结束,它都在进行工作,那么总共消耗的能量为\(P\times(a[n]-a[1]+2r)\)

然后我们需要求出所有\(\gt 2r\)的间隔减去\(2r\)后的总和,这就是探测器不进行工作的时间长度


对于所有的\(n-1\)个间隔,先设\(\gt 2r\)的间隔数量为\(upperCount\)

如果我们能够找出所有\(\gt 2r\)的间隔的长度总和\(uselessLen\),就可以通过\(uselessLen-2r\times upperCount\)来得到上述的探测器不进行工作的时间长度


对于\(\gt 2r\)的间隔数量与\(\gt 2r\)的间隔长度总和,可以通过差分数组树状数组进行维护前缀和

如果某两辆车的间隔为\(d\),可以在维护数量的数组里\(d\)位置\(+1\),在维护长度的数组里\(d\)位置\(+d\)

在取值时,\(arrayCount[2r]\)可以表示长度\(\le 2r\)的间隔数量,故\(upperCount=(n-1)-arrayCount[2r]\)

\(arrayLen[2r]\)可以表示长度\(\le 2r\)的间隔长度总和,故\(uselessLen=(a[n]-a[1]+ar)-arrayLen[2r]-2r\times upperCount\)




程序

使用了差分数组进行维护前缀和

(注意有些读入挂可能会WA Case 1)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int a[100050];
ll ar1[2000050],ar2[2000050];

int main()
{
    int n,m,P,C;
    scanf("%d%d%d%d",&n,&m,&P,&C);
    
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(i!=1)
        {
            int d=a[i+1]-a[i];
            ar1[d]++;
            ar2[d]+=d;
        }
    }
    
    for(int i=1;i<=2000000;i++)
    {
        ar1[i]+=ar1[i-1];
        ar2[i]+=ar2[i-1];
    }
    
    int TotalLen=a[n]-a[1];
    for(int i=1;i<=m;i++)
    {
        int r;
        scanf("%d",&r);
        if(r>=1000000) //这种探测器不会关闭,不需要讨论
        {
            printf("%lld\n",1LL*P*(TotalLen+2*r)+C);
            continue;
        }
        int upperCount=(n-1)-ar1[2*r]; //求出>2r的间隔数量
        int uselessLen=TotalLen-ar2[2*r]; //求出>2r的间隔长度-2r后的和
        int Len=(TotalLen+2*r)-(uselessLen-2*r*upperCount); //可行长度
        printf("%lld\n",1LL*P*Len+1LL*C*(upperCount+1)); //一开始就需要进行一次开启
    }
    
    return 0;
}

上一篇:安装sass(css预处理语言)


下一篇:ZJNU 2184 - 最长子串 (并查集)