• [洛谷 P3992] [BJOI2017]开车


    [BJOI2017]开车

    P3992

    1

    题目大意

    给出 (n) 个车的位置 (a_i)(n) 个加油站的位置 (b_i) ,一辆车从 (x) 位置到 (y) 位置的代价为 (|x - y|) ,问如何两两配对使答案最小,(q) 次询问每次修改 (a_x)(y) ,每次修改后给出当前答案。

    数据范围

    (n le 50000, q le 50000)

    时空限制

    3000ms, 256MB, O2

    反思

    似乎根本没有任何思考就点开了题解

    分析

    首先发现答案实际上就是分别排序后对应配对,考虑维护数轴上每条边的贡献,发现我们记一个 (a_i)(1) ,$b_i $ 为 (-1) 的前缀和,那么它的贡献就是 (|sum|)

    那么我们修改就相当于区间 (pm 1) ,但是我们无法简单地维护一个绝对值,所以用分块的方法

    将块内的前缀和排序,二分 (0) 点,带上标记即可修改

    Code

    #include <algorithm> 
    #include <cmath>
    #include <cstdio> 
    #include <iostream>
    using namespace std;
    inline char nc() {
    	static char buf[100000], *l = buf, *r = buf;
    	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
    } 
    template<class T> void read(T &x) {
    	x = 0; int f = 1, ch = nc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
    	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
    	x *= f;
    }
    #define myabs(a) ((a) < 0 ? - (a) : (a))
    typedef long long ll;
    const int maxn = 50000 + 5;
    const int maxq = 50000 + 5;
    const int maxp = maxn * 2 + maxq;
    const int maxb = 400;
    int n, q, a[maxn], b[maxn]; ll an;
    int H[maxp], p;
    struct data {
    	int x, y;
    } ask[maxq];
    void Hash() {
    	sort(H + 1, H + p + 1);
    	p = unique(H + 1, H + p + 1) - H - 1;
    	for(int i = 1; i <= n; ++i) {
    		a[i] = lower_bound(H + 1, H + p + 1, a[i]) - H;
    	}
    	for(int i = 1; i <= n; ++i) {
    		b[i] = lower_bound(H + 1, H + p + 1, b[i]) - H;
    	}
    	for(int i = 1; i <= q; ++i) {
    		ask[i].y = lower_bound(H + 1, H + p + 1, ask[i].y) - H;
    	}
    }
    namespace blocks {
    	int len;
    	int L[maxb], R[maxb], bel[maxp];
    	int sum[maxp], val[maxp];
    	int sval[maxp], ord[maxp], tag[maxb];
    	inline int cmp(const int &a, const int &b) {
    		return sum[a] < sum[b];
    	} 
    	void rebuild(int x) {
    		sort(ord + L[x], ord + R[x] + 1, cmp);
    		sval[L[x]] = val[ord[L[x]]];
    		for(int i = L[x] + 1; i <= R[x]; ++i) {
    			sval[i] = sval[i - 1] + val[ord[i]];
    		}
    	}
    	void init() {
    		for(int i = 1; i <= n; ++i) {
    			sum[a[i]]++, sum[b[i]]--;
    		}
    		for(int i = 1; i <= p; ++i) {
    			if(i < p) val[i] = H[i + 1] - H[i];
    			sum[i] += sum[i - 1];
    			an += (ll)myabs(sum[i]) * val[i];
    		}
    		len = ceil(sqrt(p));
    		for(int i = 1; i <= p; ++i) {
    			int x = bel[i] = (i - 1) / len + 1;
    			if(!L[x]) L[x] = i;
    			R[x] = i;
    		}
    		for(int i = 1; i <= p; ++i) ord[i] = i;
    		for(int i = 1; i <= bel[p]; ++i) {
    			rebuild(i);
    		}
    	}
    	void add(int x) {
    		for(int i = x; i <= R[bel[x]]; ++i) {
    			an += val[i] * (sum[i] + tag[bel[x]] >= 0 ? 1 : -1);
    			sum[i]++;
    		}
    		rebuild(bel[x]);
    		for(int i = bel[x] + 1; i <= bel[p]; ++i) {
    			int l = L[i], r = R[i], re = -1;
    			while(l <= r) {
    				int mid = (l + r) >> 1;
    				if(sum[ord[mid]] + tag[i] >= 0) r = mid - 1, re = mid;
    				else l = mid + 1;
    			}
    			if(re == -1) {
    				an -= sval[R[i]];
    			}
    			else if(re == L[i]) {
    				an += sval[R[i]];
    			}
    			else {
    				an -= sval[re - 1];
    				an += sval[R[i]] - sval[re - 1];
    			}
    			tag[i]++;
    		}
    	}
    	void sub(int x) {
    		for(int i = x; i <= R[bel[x]]; ++i) {
    			an += val[i] * (sum[i] + tag[bel[x]] <= 0 ? 1 : -1);
    			sum[i]--;
    		}
    		rebuild(bel[x]);
    		for(int i = bel[x] + 1; i <= bel[p]; ++i) {
    			int l = L[i], r = R[i], re = -1;
    			while(l <= r) {
    				int mid = (l + r) >> 1;
    				if(sum[ord[mid]] + tag[i] <= 0) l = mid + 1, re = mid;
    				else r = mid - 1;
    			}
    			if(re == -1) {
    				an -= sval[R[i]];
    			}
    			else if(re == R[i]) {
    				an += sval[R[i]];
    			}
    			else {
    				an += sval[re];
    				an -= sval[R[i]] - sval[re];
    			}
    			tag[i]--;
    		}
    	}
    }
    int main() {
    //	freopen("testdata.in", "r", stdin);
    //	freopen("testdata.out", "w", stdout); 
    	read(n);
    	for(int i = 1; i <= n; ++i) {
    		read(a[i]), H[++p] = a[i];
    	}
    	for(int i = 1; i <= n; ++i) {
    		read(b[i]), H[++p] = b[i];
    	}
    	read(q); 
    	for(int i = 1; i <= q; ++i) {
    		read(ask[i].x), read(ask[i].y);
    		H[++p] = ask[i].y;
    	}
    	Hash();
    	blocks :: init();
    	printf("%lld
    ", an);
    	for(int i = 1; i <= q; ++i) {
    		int x = ask[i].x, y = ask[i].y;
    		blocks :: sub(a[x]);
    		blocks :: add(a[x] = y);
    		printf("%lld
    ", an);
    	}
    	return 0;
    }
    

    总结

    利用分块维护绝对值

  • 相关阅读:
    每日日报8月12日
    每日日报8月15日
    每日日报8月18日
    每日日报8月9日
    九月29号——动手又动脑
    今日总结
    每周总结
    今日总结
    周总结
    今日总结
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/10347164.html
Copyright © 2020-2023  润新知