1. 程式人生 > >柵欄(fence)

柵欄(fence)

之間 操作 ont getc spa -1 增加 blog 數根

【問題描述】
小 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)