P2672 推销员 - 线段树 - 离线处理

注意这X家不一定连续,并且输入中给定的就是第i家到入口的距离
先想只有一个的情况,扫一遍,找到其中最大的那个(路径要乘上2)
两个的呢?不大好想,不如想想n个的时候,情况最简单,那么从n到n-1的转移也很简单
考虑把过程反过来,一个个删点,每次删使得答案变化最小的点
现在问题是 快速找出区间内对答案变化贡献最小的一点及其位置,与区间目前最右端点某些信息比较后就可以得到答案
要维护这么多信息,还是区间上的,要么线段树要么平衡树
用线段树维护!
注意维护目前最右边的点的位置r_pos以及下次最右边的位置nxt
这个nxt更新要及时,但是又不能更新太多次
一开始我写了个非常慢的更新= =
这样子的:

    int nxt = r_pos - 1;
    while(vis[nxt]) nxt--;

这样每次循环的时候都会重新找一次nxt,十分的慢,不如把nxt定义在循环外面及时维护
注意删除右端点的时候,让现在nxt成为现在右端点,那么这个时候nxt应该也要发生更新

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,ans,now; 
int a[MAXN],sum_path[MAXN],sum_a[MAXN],path[MAXN],pla[MAXN],vis[MAXN];

// segment tree

struct segment{
	int mi, pos;
}tr[MAXN * 4];

void update(int w) {
	int le = w * 2, ri = w * 2 + 1;
	if(tr[le].mi < tr[ri].mi) {
		tr[w].mi = tr[le].mi;
		tr[w].pos = tr[le].pos;
	} else {
		tr[w].mi = tr[ri].mi;
		tr[w].pos = tr[ri].pos;
	}
}

void build(int w, int l, int r) {
	if(l == r) {
		tr[w].mi = a[l];
		tr[w].pos = l;
		return;
	}
	int le = w * 2, ri = w * 2 + 1, mid = (l + r) / 2;
	build(le, l, mid);
	build(ri, mid+1, r);
	update(w);
}

void del(int w, int l, int r, int pos) {
	if(l == r) {
		tr[w].mi = INF;
		return;
	}
	int le = w * 2, ri = w * 2 + 1, mid = (l + r) / 2;
	if(pos <= mid)
		del(le, l, mid, pos);
	else 
		del(ri, mid+1, r, pos);
	update(w);
}

int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; i++) {
        scanf("%d", &path[i]);
    }
    for(int i=1; i<=n; i++) {
        scanf("%d", &a[i]);
    }
    build(1, 1, n);
    for(int i=1; i<=n; i++) {
    	now += a[i];
    }
    now += path[n] * 2;
    pla[n] = now;
    int r_pos = n;
    del(1, 1, n, n);
    int nxt = n - 1;
    for(int i=n-1; i; i--) {
    	int pos = tr[1].pos;
    	int dis = (path[r_pos] - path[nxt]) * 2;
    	if(a[pos] > dis + a[r_pos]) {
    		now -= dis + a[r_pos];
    		r_pos = nxt;
    		del(1, 1, n, r_pos);
    		vis[r_pos] = 1;
    	} else {
    		now -= a[pos];
    		vis[pos] = 1;
    		del(1, 1, n, pos);
    	}
    	while(vis[nxt]) nxt--;
    	pla[i] = now;
    }
	for(int i=1; i<=n; i++) {
		printf("%d\n", pla[i]);
	}
    return 0;
}
posted @ 2018-10-28 21:20  Zolrk  阅读(127)  评论(0编辑  收藏  举报