具体解法bestcoder上都有,这里就不详细写了。
主要用到一个树状数组区间更新单点求和的问题,以前这类问题都是用线段树做的,这次研究了一下树状数组。
大概就是,记录的值为每一个点和之前一个点的差值,这样的话,以前的函数依然可以用,但是求和函数的意义发生变化。之前求和求得的是区间和,但是现在的记录方式,求得的是最后一个节点相对于0节点的变化量总和,如果0节点值恒为0,那么这样求的便是最后一个节点的值,即变成了单点查询。
附代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <algorithm>
#define ll long long int
#define ull unsigned long long int
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define delf int m=(l+r)>>1
using namespace std;
ll max(int a,int b)
{
return a>b?a:b;
}
ll min(int a,int b)
{
return a>b?b:a;
}
ll n,w,h;
ll v[150050];
ll dp[2][150050];
ll sum;
ll ss;
ll ans;
ll H;
ll s1[50050]; //维护个数,区间更新,单点查询
ll s2[50050]; //维护总和,单点更新,区间查询
int lowbit(int x)
{
return x&(-x);
}
void add1(int x,int v) //x与之前一个位置的差值+1
{
while (x<=50000)
{
s1[x]+=v;
x+=lowbit(x);
}
return ;
}
void add2(int x,int v) //单点更新
{
while (x<=50000)
{
s2[x]+=v;
x+=lowbit(x);
}
return ;
}
int gets1(int x) //单点查询
{
int s=0;
while (x>0)
{
s+=s1[x];
x-=lowbit(x);
}
return s;
}
int gets2(int x) //区间查询
{
int s=0;
while (x>0)
{
s+=s2[x];
x-=lowbit(x);
}
return s;
}
void solve(ll h1)
{
//ss1指小于h1的个数,cc1指小于h1的元素总和,ss2和cc2同理
ll ss1=gets1(h1-1);
ll ss2=w-ss1;
ll cc1=gets2(h1-1);
ll cc2=sum-cc1;
ll c=max(ss1*h1-cc1,cc2-ss2*h1);
if (c<ans)
{
ans=c;
H=h1;
}
if (c==ans&&H<h1&&h1*w<=ss) //消耗相同的情况下,取高度大的
{
ans=c;
H=h1;
}
return ;
}
int main()
{
while (~scanf("%I64d%I64d%I64d",&n,&w,&h))
{
h+=1;
for (int i=0;i<w;i++)
v[i]=v[i+n+w]=1;
sum=0;
for (int i=w;i<=n+w-1;i++)
{
scanf("%d",&v[i]);
v[i]++; //将每堆积木的增加一个,这样两端之前没有的积木高度都变为1,才可以用树状数组进行记录。
sum+=v[i];
}
ss=sum;
if (sum<w*h)
{
printf("-1\n");
continue ;
}
ans=(ll)w*h;
H=h;
sum=w;
add1(1,w);
add2(1,w);
for (int i=w;i<=n+w*2-1;i++)
{
int p=v[i-w];
sum-=p;
add1(p,-1);
add2(p,-p);
p=v[i];
sum+=p;
add1(p,1);
add2(p,p);
if (sum/w>=h)
solve(sum/w);
if (sum/w+1>=h)
solve(sum/w+1);
solve(h);
}
add1(1,-w);
add2(1,-w);
printf("%I64d %I64d\n",H-1,ans);
}
}