• bzoj 5308 [ZJOI2018] 胖


    bzoj 5308 [ZJOI2018] 胖

    Solution

    ( ext{ZJOI2018}) 最简单的一道题

    首先看数据范围,大概就是每次 (O(klog n)) 或者 (O(klog ^2n))

    不难发现,答案就是每一个修建方案对应的点能够扩展的点数之和

    就是说,对于每一个修建方案对应的点,它能够在 贝尔福特曼 算法中扩展到的点是一个区间,我们要求所有这样的区间的长度的和

    因为是一个区间,所以我们可以二分它的左右端点,关键在于如何判断当前位置能否被当前点扩展到

    假设当前点是 (a_i),当前位置是 (pos),令 (d= |a_i-pos|),那么影响到的区间为 ([a_i-d,a_i+d]),如果这个区间里面存在一个 (p),满足 (dis(p,pos) < dis(a_i,pos)),那么 (p) 肯定会优先扩展到 (pos),所以我们需要在 (O(log n)) 的时间内得到某个区间到某个点的路径长度的最小值,可以用线段树或者 ( ext{ST}) 表完成,只需要维护区间到两个端点的路径长度最小值即可

    注意如果多个点到一个位置的路径长度相同,我们需要让这个位置只被算一次,所以让这个位置被经过边数最少的点扩展,如果还有多个,我们让它被左侧的点扩展

    Code

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    ll read(){
    	ll x=0,f=1;char c=getchar();
    	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn = 200200;
    int n, m, k;
    ll a[maxn], p[maxn], le[maxn];
    ll mn1[maxn][20], mn2[maxn][20];
    pii pp[maxn];
    int lg[maxn];
    
    inline ll dis(int x, int y) {
    	if (x > y) return 1e18;
    	return a[y - 1] - a[x - 1];
    }
    inline ll calc1(int l, int r) {
    	if (r < l) return 1e18;
    	int len = lg[r - l + 1];
    	int pos = l + (1 << len) - 1, pos2 = r - (1 << len) + 1;
    	return min(mn1[l][len] + dis(p[pos], p[r]), mn1[pos2][len]);
    }
    inline ll calc2(int l, int r) {
    	if (r < l) return 1e18;
    	int len = lg[r - l + 1];
    	int pos = r - (1 << len) + 1;
    	return min(mn2[l][len], mn2[pos][len] + dis(p[l], p[pos]));
    }
    inline ll cal1(int l, int r) {
    	int pos1 = lower_bound(p + 1, p + k + 1, l) - p;
    	int pos2 = upper_bound(p + 1, p + k + 1, r) - p - 1;
    	if (pos2 < pos1) return 1e18;
    	return calc1(pos1, pos2) + dis(p[pos2], r);
    }
    inline ll cal2(int l, int r) {
    	int pos1 = lower_bound(p + 1, p + k + 1, l) - p;
    	int pos2 = upper_bound(p + 1, p + k + 1, r) - p - 1;
    	if (pos2 < pos1) return 1e18;
    	return calc2(pos1, pos2) + dis(l, p[pos1]);
    }
    inline bool pd1(int x, int c) {
    	if (c < 1 || c > n) return false;
    	int pos = p[x], d = pos - c, l = c - d, r = c + d;
    	ll zuo = cal1(l, c), you = cal2(c, r - 1);
    	ll dist = min(zuo, you), nw = dis(c, pos) + le[x];
    	if (nw < dist) return true;
    	else return false;
    }
    inline bool pd2(int x, int c) {
    	if (c < 1 || c > n) return false;
    	int pos = p[x], d = c - pos, l = c - d, r = c + d;
    	ll zuo = cal1(l + 1, c), you = cal2(c, r - 1), you2 = cal2(c, r);
    	ll dist = min(zuo, you2), nw = dis(pos, c) + le[x];
    	if (nw < dist) return true;
    	else if (nw == dist) {
    		ll dist2 = min(zuo, you);
    		if (nw == dist2) return false;
    		else return true;
    	}
    	else return false;
    }
    void work(){
    	n = read(), m = read();
    	rep(i, 1, n - 1) a[i] = read(), a[i] += a[i - 1];
    	rep(i, 2, n) lg[i] = lg[i >> 1] + 1;
    	while (m--) {
    		k = read();
    		rep(i, 1, k) {
    			int pos = read(), l = read();
    			pp[i] = mp(pos, l);
    		}
    		sort(pp + 1, pp + k + 1);
    		rep(i, 1, k) {
    			p[i] = pp[i].fi, le[i] = pp[i].se;
    			mn1[i][0] = le[i]; mn2[i][0] = le[i];
    		}
    		rep(j, 1, 18) {
    			rep(i, 1, k) {
    				if (i + (1 << j) - 1 > k) break;
    				int d = i + (1 << (j - 1)), e = i + (1 << j) - 1;
    				mn1[i][j] = min(mn1[i][j - 1] + dis(p[d - 1], p[e]), mn1[d][j - 1]);
    				mn2[i][j] = min(mn2[i][j - 1], mn2[d][j - 1] + dis(p[i], p[d]));
    			}
    		}
    		ll ans = 0;
    		rep(i, 1, k) {
    			int L = 1, R = p[i];
    			while (L + 1 < R) {
    				int md = (L + R) >> 1;
    				if (pd1(i, md)) R = md;
    				else L = md + 1;
    			}
    			if (pd1(i, R - 1)) R--;
    			int p1 = R;
    			L = p[i], R = n;
    			while (L + 1 < R) {
    				int md = (L + R) >> 1;
    				if (pd2(i, md)) L = md;
    				else R = md - 1;
    			}
    			if (pd2(i, L + 1)) L++;
    			ans += L - p1 + 1;
    			// printf("%d %d %d
    ", i, p1, L);
    		}
    		printf("%lld
    ", ans);
    	}
    }
    
    int main(){
    	#ifdef LZT
    		freopen("in","r",stdin);
    	#endif
    	
    	work();
    	
    	#ifdef LZT
    		Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
    	#endif
    }
    

    Review

    想法比较自然,关键在于如何维护区间到某个点的距离的最值

  • 相关阅读:
    maven 3.2.5 的安装,部署和实例
    Java8 stream操作toMap的key重复问题
    Jenkins配置定时任务注意点
    npm install提示node-sass错误
    centos 使用docker 安装 teamcity
    centos 不能连接外网,使用本地yum源安装软件
    git添加本地代码到远程仓库
    mysql 新建外网用户 和只读用户
    mysql 删除重复数据保留最新一条
    批量删除redis缓存
  • 原文地址:https://www.cnblogs.com/wawawa8/p/10225500.html
Copyright © 2020-2023  润新知