ZJNU 2479 - 車輛探測器 (差分陣列/樹狀陣列)
浙江師範大學第十八屆大學生程式設計競賽(重現) H - 車輛探測器
題意
\(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\)
先假設探測器從第\(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;
}