题解 柱状图
考场只会 \(n^3\) 暴力
- 单峰/单谷函数相加还是单峰/单谷函数,证明的话,以单谷为例,在每个拐点斜率都是单增的
常用于处理绝对值之类的东西
于是如果最高的位置是 \(i\),高度为 \(h\),则操作数为 \(\sum |h_j-(h_i-|i-j|)|\)
发现如果能把里边的绝对值拆开的话,整体是个单谷函数,可以三分
于是枚举最高点的位置,三分check
这里维护最高点左右两边的树状数组用到了广义扫描线的思想
扫到一个最高时把它从右侧删除,加到左侧
- 对一个要求支持动态插入的序列离散化(对一个要求支持动态插入的序列用树状数组维护大小关系):
可以先离线离散化,每次对新插入的元素lower_bound求排名
三分板子:
int lside=0, rside=n, lmid, rmid;
while (lside<=rside) {
rmid=(lside+rside)>>1, lmid=rmid-1;
if (lmid<=lside || rmid>=rside) break;
if (calc(lmid)<calc(rmid)) rside=rmid;
else lside=lmid;
}
for (int i=lside; i<=rside; ++i) ans=min(ans, calc(i));
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll x[N];
namespace force{
void solve() {
ll ans=INF;
for (int i=1; i<=n; ++i) {
for (int j=1; j<=n; ++j) {
ll maxh=x[i]+abs(j-i), dlt=0;
for (int k=1; k<=n; ++k) {
if (maxh-abs(j-k)<=0) goto jump;
dlt+=llabs( maxh-abs(j-k) - x[k] );
if (dlt>=ans) goto jump;
}
ans=min(ans, dlt);
jump: ;
}
}
printf("%lld\n", ans);
exit(0);
}
}
namespace task{
ll lst1[N], lst2[N], bin1[N], bin2[N], cnt1[N], cnt2[N], ans=INF;
inline void upd(ll* bin, int i, ll dat) {for (; i<=n; i+=i&-i) bin[i]+=dat;}
inline ll query(ll* bin, int i) {ll ans=0; for (; i; i-=i&-i) ans+=bin[i]; return ans;}
inline ll calc(int i, ll hi) {
// cout<<"calc: "<<i<<' '<<hi<<endl;
if (hi-i+1<=0 || hi-n+i<=0) return INF;
ll ans=0; int pos;
pos=lower_bound(lst1+1, lst1+n+1, hi-i)-lst1;
if (pos>1) ans+=(hi-i)*query(cnt1, pos-1)-query(bin1, pos-1);
if (pos<n) ans+=query(bin1, n)-query(bin1, pos-1)-(hi-i)*(query(cnt1, n)-query(cnt1, pos-1));
// cout<<"ans1: "<<ans<<endl;
pos=lower_bound(lst2+1, lst2+n+1, hi+i)-lst2;
// cout<<"pos: "<<pos<<endl;
if (pos>1) ans+=(hi+i)*query(cnt2, pos-1)-query(bin2, pos-1);
if (pos<n) ans+=query(bin2, n)-query(bin2, pos-1)-(hi+i)*(query(cnt2, n)-query(cnt2, pos-1));
ans+=llabs(hi-x[i]);
// cout<<"return "<<ans<<endl;
return ans;
}
void solve() {
for (int i=1; i<=n; ++i) lst1[i]=x[i]-i, lst2[i]=x[i]+i;
sort(lst1+1, lst1+n+1); sort(lst2+1, lst2+n+1);
for (int i=1; i<=n; ++i) {
upd(bin2, lower_bound(lst2+1, lst2+n+1, x[i]+i)-lst2, x[i]+i);
upd(cnt2, lower_bound(lst2+1, lst2+n+1, x[i]+i)-lst2, 1);
}
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
if (i!=1) {
upd(bin1, lower_bound(lst1+1, lst1+n+1, x[i-1]-i+1)-lst1, x[i-1]-i+1);
upd(cnt1, lower_bound(lst1+1, lst1+n+1, x[i-1]-i+1)-lst1, 1);
}
upd(bin2, lower_bound(lst2+1, lst2+n+1, x[i]+i)-lst2, -x[i]-i);
upd(cnt2, lower_bound(lst2+1, lst2+n+1, x[i]+i)-lst2, -1);
// cout<<"bin1: "; for (int i=1; i<=n; ++i) cout<<query(bin1, i)<<' '; cout<<endl;
// cout<<"bin2: "; for (int i=1; i<=n; ++i) cout<<query(bin2, i)<<' '; cout<<endl;
ll lside=0, rside=1e10, lmid, rmid;
while (lside<=rside) {
rmid=(lside+rside)>>1; lmid=rmid-1;
if (lmid<=lside || rmid>=rside) break;
if (calc(i, lmid)<calc(i, rmid)) rside=rmid;
else lside=lmid;
}
// cout<<"lr: "<<lside<<' '<<rside<<endl;
for (int j=lside; j<=rside; ++j) ans=min(ans, calc(i, j));
}
printf("%lld\n", ans);
exit(0);
}
}
signed main()
{
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) x[i]=read();
// force::solve();
task::solve();
return 0;
}