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;
}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值