Vijos1459 車展

點擊進入原題

題意:給定一個序列。要求對一個區間的數進行如下操作:把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;
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章