題意:給定一個序列。要求對一個區間的數進行如下操作:把i~j這段數字全部設爲,使得代價最小。每次操作後,數列恢復成原始狀態。求所有操作的最小代價和。
首先我們來考慮一下應該如何選取。方便起見,我們就先考慮,先對這個區間排序,我們得到了一個有序序列。
假設存在,那麼可以求得代價。
化簡得,,
因爲代價要小,所以我們要使最大,
我們令,原式就轉化成。
那麼這一項要儘可能大。因爲,所以。
因此隨着d的增大而減小。所以w隨着d的增大而增大。
同理,時,可以發現w隨着d的增大而減小。
因此,,w取到最小值。此時m即爲中位數。同理對於每個區間,我們都要取這個區間的中位數。(如果這個序列只有偶數個數,那麼取第n/2個,或者第n/2+1個都可以,沒有差別的。)
觀察到毫無壓力。所以我們可以考慮預處理每個區間的中位數,再預處理每個區間的和減去中位數的絕對值的和。回答詢問的時候即可。
枚舉每一個數,向左掃,記錄枚舉到時大於等於的個數-比小的個數。向右掃,記錄枚舉到時大於等於的個數-比小的個數。
如果是的中位數,那麼下列兩種條件必定滿足一種:
1. 是奇數,且
2. 是偶數,且
那麼先往左掃,然後用類似Hash的鏈表存下,然後向右掃的時候查詢左邊的情況,看看能否滿足上述條件的一種。然後存下答案就行。
最後預處理每個區間的和減去中位數的絕對值的和。
綜上,預處理複雜度,詢問複雜度 。整個算法的時間複雜度就是。
最後貼上代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
int m[maxn*3],pre[maxn],mid[maxn][maxn],a[maxn],id[maxn],n,q;
long long sum[maxn][maxn];
inline int myabs(int x){ return (x<0)?(-x):(x);}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
memset(m,-1,sizeof(m));
memset(id,0,sizeof(id));
memset(pre,0,sizeof(pre));
int d=0,x=0,tot=1;
for(int j=i;j>=1;j--){
if(a[j]>a[i]) d++; else x++;
id[tot]=j; pre[tot]=m[d-x+maxn]; m[d-x+maxn]=tot++;
}
d=0,x=-1;
for(int j=i;j<=n;j++){
if(a[j]>a[i]) d++; else x++;
for(int p=m[x-d+maxn];p!=-1;p=pre[p]) if(((j-id[p]+1)%2)==0) mid[id[p]][j]=i;
for(int p=m[x-d-1+maxn];p!=-1;p=pre[p]) if(((j-id[p])%2)==0) mid[id[p]][j]=i;
}
}
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) sum[i][j]=sum[i][j-1]+1LL*myabs(a[j]-a[i]);
int x,y;
long long ans=0;
while(q--){
scanf("%d%d",&x,&y);
ans+=sum[mid[x][y]][y]-sum[mid[x][y]][x-1];
}
cout<<ans<<endl;
return 0;
}