题解 柱状图

传送门

考场只会 \(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;
}
posted @ 2021-09-22 20:34  Administrator-09  阅读(1)  评论(0编辑  收藏  举报