1. 程式人生 > 實用技巧 >ZJNU 2479 - 車輛探測器 (差分陣列/樹狀陣列)

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;
}