柵欄(fence)
【問題描述】
小 v 家有一條柵欄,由 n 個木板順序組成,第 i 個木板的高度是 Ai。現
在小鎮上流行在柵欄上畫矩形,所以小 v 也要在自家的柵欄上畫。若要在區間
[x,x+k-1]這個區間畫一個寬度為 k 的矩形(1≤x≤n-k+1),為了美觀,高度一
定是這個區間裏高度最低的木板。現在小 v 心中有 m 個理想的寬度,第 i 個為
Ki,(Ki 與 Kj 之間可能一樣)。他想知道對於每個 Ki,其矩形高度的期望。
【輸入格式】
第一行一個整數 n,表示木板的數目。
第二行有 n 個正整數,第 i 個數表示第 i 個木板的高度。
第三行一個整數 m,表示理想寬度的數目。
第四行有 m 個正整數,第 i 個數表示小 v 心中理想的第 i 個寬度 Ki。
【輸出格式】
輸出 m 行實數,第 i 行表示寬度為 Ki 的矩形高度的期望,只要你的答案和
正確答案的差的絕對值小於 1e-6,你的答案將被視為正確。
【輸入樣例】
3
3 2 1
4
1 2 3 1
【輸出樣例】
2.000000000000000
1.500000000000000
1.000000000000000
2.000000000000000
【數據範圍】
對於 30%的數據,n≤20;m≤20;
對於 40%的數據,n≤2000;m≤2000;
對於 70%的數據,n≤100000;m≤100000;
對於 100%的數據,n≤1000000;m≤1000000;1≤Ai≤10 9 ;1≤Ki≤n;
輸入較大,C++選手請使用讀入優化。
%%%YZD大佬,考場怒切此題
本題要用到把差分數組差分和單調棧的技巧
0 x j i
|————|—————|—————|——————
假設我們維護一個單調遞增的棧,當前點為i,j=q[tail],x=q[tail-1]
我們為了方便,寫為[i,i]*[i,n]=s[i]
意思是以[i,i]為左端點,[i,n]為右端點的所有區間最小值為s[i]
如果h[i]<h[j],那麽說明,之前我們認為的[x+1,j]*[i,n]=s[j]是錯誤的,我們要減去這些情況
再加上[x+1,j]*[i,n]=s[i]的情況
彈出j,執行單調棧操作
直到h[i]>h[j],在加入[i,i]*[i,n]=s[i]的情況
為什麽不要減去[j+1,i-1]*[i,n]?因為我們在讀到i之前,[j+1,i-1]已經被處理完了
現在問題只有:怎樣增加刪除情況
令f[i]為長度為i的所有區間最小值的總和
我們來看一下[1,2]*[3,5]=s[i]的情況
長為2:2~3 f[2]+=s[i];
長為3:1~3,2~4 f[3]+=2*s[i];
長為4:1~4,2~5 f[4]+=2*s[i]
長為5:1~5 f[5]+=1*s[i]
我們發現,系數根據區間長呈勾函數
1 2 2 1
差分一次
1 1 0 -1 -1 0
二次差分
1 0 -1 -1 0 1
為什麽要二次差分?
1 2 3 4 5 5 4 3 2 1
差分一次:1 1 1 1 1 0 -1 -1 -1 -1 -1
涉及了線段樹區間操作,顯然超時
但若再差分一次:1 0 0 0 0 -1 -1 0 0 0 0 1
就只要修改4個點
把二次差分拓展到[x+1,j]*[i,n]
最短的區間長是i-j+1,f[i-j+1]+=s[i]
最長的是區間長是n-x,f[n-x+2]+=s[i]
f[i-k+1]-=s[i]
f[n-k+2]-=s[i]
因為還要減去s[j],所以將s[i]變成s[i]-s[j]就行
對於[i,i]×[i,n]=s[i]的系數
1 1 1 1 1 1 1(n-i+1) 0 0
差分:1 0 0 0 0 0 0 0 0 -1 0
再差分:1 -1 0 0 0 0 0 0 0 -1 1
就等價於:f[1]+=s[i],f[2]-=s[i],f[n-i+2]-=s[i],f[n-i+3]+=s[i]
得到最後的數組只要求兩次前綴和就行了
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 double f[1000001]; 7 int q[1000001],tail,i,s[1000001],n,m; 8 int gi() 9 { 10 char ch=getchar(); 11 while (ch<‘0‘||ch>‘9‘) ch=getchar(); 12 int x=0; 13 while (ch>=‘0‘&&ch<=‘9‘) 14 { 15 x=x*10+ch-‘0‘; 16 ch=getchar(); 17 } 18 return x; 19 } 20 21 int main() 22 {int i,k; 23 freopen("fence.in","r",stdin); 24 freopen("fence.out","w",stdout); 25 cin>>n; 26 for (i=1;i<=n;i++) 27 { 28 s[i]=gi(); 29 while (tail&&s[q[tail]]>s[i]) 30 { 31 int j=q[tail],k=q[tail-1]; 32 f[i-j+1]+=s[i]-s[j]; 33 f[n-k+2]+=s[i]-s[j]; 34 f[n-j+2]+=s[j]-s[i]; 35 f[i-k+1]+=s[j]-s[i]; 36 tail--; 37 } 38 tail++; 39 q[tail]=i; 40 f[1]+=s[i];f[2]-=s[i]; 41 f[n-i+3]+=s[i];f[n-i+2]-=s[i]; 42 } 43 for (i=1;i<=n;i++) 44 f[i]+=f[i-1]; 45 for (i=1;i<=n;i++) 46 f[i]+=f[i-1]; 47 cin>>m; 48 for (i=1;i<=m;i++) 49 { 50 k=gi(); 51 printf("%.15lf\n",f[k]/(double)(n-k+1)); 52 } 53 }
柵欄(fence)