UOJ#410/LOJ2868【IOI2018】会议 笛卡尔树+线段树

版权声明:本文是蒟蒻写的,转载。。。随便吧 https://blog.csdn.net/xgc_woker/article/details/87091134

Description
N N 座山横着排成一行,从左到右编号为从 0 0 N 1 N−1 。山 i i 的高度为 H i 0 i N 1 Hi( 0≤i≤N−1 ) 。每座山的顶上恰好住着一个人。

你打算举行 Q Q 个会议,编号为从 0 0 Q 1 Q−1 。会议 j 0 j Q 1 j( 0≤j≤Q−1 ) 的参加者为住在从山 L j Lj 到山 R j Rj (包括 LjLj 和 RjRj )上的人 0 L j R j N 1 ( 0≤Lj≤Rj≤N−1 ) 。对于该会议,你必须选择某个山 x x 做为会议举办地 L j x R j ( Lj≤x≤Rj ) 。举办该会议的成本与你的选择有关,其计算方式如下:

来自每座山 y L j y R j y(Lj≤y≤Rj) 的参会者的成本,等于在山 x x 和 yy 之间(包含 x x y y )的所有山的最大高度。特别地,来自山 x x 的参会者的成本是 H x Hx ,也就是山 x x 的高度。
会议的成本等于其所有参会者的成本之和。
你想要用最低的成本来举办每个会议。

注意,所有的参会者将在每次会议后回到他们自己的山;所以一个会议的成本不会受到先前会议的影响。


Sample Input
4 2
2 4 3 5
0 2
1 3


Sample Output
10
12


L O J LOJ 可以以传统方式提交。

我们考虑离线完成这个问题,我们把一个询问挂在这个区间的最大值上。
设区间 [ l , r ] [l,r] 的答案为 f [ l ] [ r ] f[l][r] ,最大值的位置为 m i d mid
那么 f [ l ] [ r ] = m i n ( f [ l ] [ m i d 1 ] + ( r m i d + 1 ) h [ m i d ] , f [ m i d + 1 ] [ r ] + ( m i d l + 1 ) h [ m i d ] ) f[l][r]=min(f[l][mid-1]+(r-mid+1)*h[mid],f[mid+1][r]+(mid-l+1)*h[mid])
那么这个结构就是一棵笛卡尔树。
对于每一个右端点,或左端点,我们考虑维护他到当前分治中心的答案。
左端点或右端点的问题你可以把这个串正反跑两遍,相当于你每次只用维护某个点作为右端点到分治中心的答案即可。

考虑更新某一段区间到分治中心的答案。
设我当前区间为 [ l , r ] [l,r] ,分治中心为 [ l , r ] [l,r]
那么我们对于线段树上的 [ m i d + 1 , r ] [mid+1,r] 中的每一个 i i 都要让他们的值变成:
f [ l ] [ i ] = m i n ( f [ l ] [ m i d 1 ] + ( i m i d + 1 ) h [ m i d ] , f [ m i d + 1 ] [ i ] + ( m i d l + 1 ) h [ m i d ] ) f[l][i]=min(f[l][mid-1]+(i-mid+1)*h[mid],f[mid+1][i]+(mid-l+1)*h[mid])
事实上这个区间中肯定会以一个点作为分界线,满足左边的值都取 f [ l ] [ m i d 1 ] + ( r m i d + 1 ) h [ m i d ] f[l][mid-1]+(r-mid+1)*h[mid] ,右边的值都取 f [ m i d + 1 ] [ i ] + ( m i d l + 1 ) h [ m i d ] f[mid+1][i]+(mid-l+1)*h[mid]
那么可以通过在线段树上二分实现这个修改。

考虑证明上面的结论:
f [ l ] [ m i d 1 ] + ( i m i d + 1 ) h [ m i d ] f [ m i d + 1 ] [ i ] ( m i d l + 1 ) h [ m i d ] < = f [ l ] [ m i d 1 ] + ( ( i + 1 ) m i d + 1 ) h [ m i d ] f [ m i d + 1 ] [ i + 1 ] ( m i d l + 1 ) h [ m i d ] f[l][mid-1]+(i-mid+1)*h[mid]-f[mid+1][i]-(mid-l+1)*h[mid] <= f[l][mid-1]+((i+1)-mid+1)*h[mid]-f[mid+1][i+1]-(mid-l+1)*h[mid]
= > =>
f [ m i d + 1 ] [ i + 1 ] f [ m i d + 1 ] [ i ] < = h [ m i d ] f[mid+1][i+1]-f[mid+1][i] <= h[mid]
这个式子显然是满足的,即得证。


L O J LOJ 代码, U O J UOJ 简单魔改就好了。

#include <ctime>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
LL _min(LL x, LL y) {return x < y ? x : y;}
const int N = 750001;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

LL ans[N];
int n, Q, h[N];
struct st_table {
	int f[20][N], bin[21], Log[N];
	
	int _max(int x, int y) {return h[x] >= h[y] ? x : y;}
	
	void bt() {
		bin[0] = 1; for(int i = 1; i <= 20; i++) bin[i] = bin[i - 1] * 2;
		Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
		for(int i = 1; i <= n; i++) f[0][i] = i;
		for(int i = 1; bin[i] <= n; i++) {
			for(int j = 1; j <= n - bin[i] + 1; j++) {
				f[i][j] = _max(f[i - 1][j], f[i - 1][j + bin[i - 1]]);
			}
		}
	}
	
	int query(int l, int r) {
		int hh = Log[r - l + 1];
		return _max(f[hh][l], f[hh][r - bin[hh] + 1]);
	}
} st;
struct query{int l, r;} g[N];
struct cc {LL a, b;};
vector<int> q[N];
struct tnode {
	int lc, rc; LL c;
	cc ad, cg;
};
struct segment {
	tnode t[N << 1];
	cc n1, n2;
	int cnt;
	
	void bt(int l, int r) {
		int now = ++cnt;
		t[now].lc = t[now].rc = -1;
		t[now].c = 0, t[now].ad = t[now].cg = cc{0, 0};
		if(l < r) {
			int mid = (l + r) / 2;
			t[now].lc = cnt + 1; bt(l, mid);
			t[now].rc = cnt + 1; bt(mid + 1, r);
		}
	}
	
	void build() {cnt = 0; bt(1, n);}
	
	void change(int now, int r, cc x) {
		t[now].cg = x; t[now].ad = cc{0, 0};
		t[now].c = x.a + x.b * r;
	}
	
	void add(int now, int r, cc x) {
		if(t[now].cg.a || t[now].cg.b) t[now].cg.a += x.a, t[now].cg.b += x.b;
		else t[now].ad.a += x.a, t[now].ad.b += x.b;
		t[now].c += x.a + x.b * r;
	}
	
	void pushdown(int now, int l, int r) {
		int mid = (l + r) / 2;
		if(t[now].cg.a || t[now].cg.b) {
			change(t[now].lc, mid, t[now].cg);
			change(t[now].rc, r, t[now].cg);
			t[now].cg = cc{0, 0};
		} if(t[now].ad.a || t[now].ad.b) {
			add(t[now].lc, mid, t[now].ad);
			add(t[now].rc, r, t[now].ad);
			t[now].ad = cc{0, 0};
		}
	}
	
	void Link(int now, int l, int r, int p, LL c) {
		if(l == r) {t[now].c = c; return ;}
		pushdown(now, l, r);
		int mid = (l + r) / 2;
		if(p <= mid) Link(t[now].lc, l, mid, p, c);
		else Link(t[now].rc, mid + 1, r, p, c);
		t[now].c = t[t[now].rc].c;
	}
	
	LL query(int now, int l, int r, int p) {
		if(l == r) return t[now].c;
		pushdown(now, l, r);
		int mid = (l + r) / 2;
		if(p <= mid) return query(t[now].lc, l, mid, p);
		else return query(t[now].rc, mid + 1, r, p);
	}
	
	void G1(int now, int l, int r, int ll, int rr) {
		if(ll == l && rr == r) {add(now, r, n1); return ;}
		pushdown(now, l, r);
		int mid = (l + r) / 2;
		if(rr <= mid) G1(t[now].lc, l, mid, ll, rr);
		else if(ll > mid) G1(t[now].rc, mid + 1, r, ll, rr);
		else G1(t[now].lc, l, mid, ll, mid), G1(t[now].rc, mid + 1, r, mid + 1, rr);
		t[now].c = t[t[now].rc].c;
	}
	
	void G2(int now, int l, int r, int ll, int rr) {
		if(ll == l && rr == r) {change(now, r, n2); return ;}
		pushdown(now, l, r);
		int mid = (l + r) / 2;
		if(rr <= mid) G2(t[now].lc, l, mid, ll, rr);
		else if(ll > mid) G2(t[now].rc, mid + 1, r, ll, rr);
		else G2(t[now].lc, l, mid, ll, mid), G2(t[now].rc, mid + 1, r, mid + 1, rr);
		t[now].c = t[t[now].rc].c;
	}
	
	void gai(int now, int l, int r, int ll, int rr) {
		if(l == r) {
			t[now].c = _min(n2.a + n2.b * l, t[now].c + n1.a);
			return ;
		} pushdown(now, l, r);
		int mid = (l + r) / 2;
		if(rr <= mid) gai(t[now].lc, l, mid, ll, rr);
		else if(ll > mid) gai(t[now].rc, mid + 1, r, ll, rr);
		else {
			if(t[t[now].lc].c + n1.a <= n2.a + n2.b * mid) {
				gai(t[now].lc, l, mid, ll, mid);
				G1(t[now].rc, mid + 1, r, mid + 1, rr);
			} else {
				gai(t[now].rc, mid + 1, r, mid + 1, rr);
				G2(t[now].lc, l, mid, ll, mid);
			}
		} t[now].c = t[t[now].rc].c;
	}
	
	void modify(int l, int r, LL a, LL b, LL c) {
		n1.a = b, n1.b = 0;
		n2.a = a - c * l + c, n2.b = c;
		gai(1, 1, n, l, r);
	}
} t;


LL dfs(int l, int r) {
	if(l > r) return 0;
	if(l == r) {t.Link(1, 1, n, l, h[l]); return h[l];}
	int mid = st.query(l, r);
	LL lans = dfs(l, mid - 1);
	LL rans = dfs(mid + 1, r);
	t.Link(1, 1, n, mid, lans + h[mid]);
	for(int i = 0; i < q[mid].size(); i++) {
		int x = q[mid][i];
		int ll = g[x].l, rr = g[x].r;
		if(mid < rr) ans[x] = _min(ans[x], t.query(1, 1, n, rr) + (LL)(mid - ll + 1) * h[mid]);
	} if(mid < r) t.modify(mid + 1, r, lans + h[mid], (LL)h[mid] * (mid - l + 1), h[mid]);
	return t.query(1, 1, n, r);
}

void solve() {
	st.bt();
	for(int i = 1; i <= n; i++) q[i].clear();
	for(int i = 1; i <= Q; i++) {
		if(g[i].l == g[i].r) ans[i] = h[g[i].l];
		else q[st.query(g[i].l, g[i].r)].push_back(i);
	} t.build();
	dfs(1, n);
}

int main() {
	memset(ans, 63, sizeof(ans));
	n = read(), Q = read();
	for(int i = 1; i <= n; i++) h[i] = read();
	for(int i = 1; i <= Q; i++) g[i].l = read() + 1, g[i].r = read() + 1;
	solve();
	reverse(h + 1, h + n + 1);
	for(int i = 1; i <= Q; i++) g[i].l = n - g[i].l + 1, g[i].r = n - g[i].r + 1, swap(g[i].l, g[i].r);
	solve();
	for(int i = 1; i <= Q; i++) printf("%lld\n", ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/87091134