zoukankan      html  css  js  c++  java
  • [APIO2015]八邻旁之桥

    注意到 (K = 1, 2) 于是我们可以从简单的 (K = 1) 开始入手。可以发现家和办公室在同一边的人不管建不建桥都是无所谓的,因此下面我们只需要考虑不在同一边的人,假设桥的位置在 (d) 那么答案可以简单的表示为:

    [sumlimits_{i = 1} ^ n |S_i - d| + |T_i - d| ]

    这可以看作是 (2n) 个在数轴上的点到 (d) 的距离,这是一个很经典的问题,只需取这 (2n) 个点的中位数即可。

    接下来再考虑 (K = 2) 的情况,如果我们想直接求出两座桥的位置是很困难的,但是可以发现这样一件事,在两个相邻的点之间任意一个点建第一座桥的答案都将是一样的,那么这意味着我们第一座桥的有效位置只有 (2n) 个,那么我们能否通过枚举这 (2n) 个位置来算出在第一座桥确定的情况下第二座桥最优的位置呢?可以发现这也是一个很困难的事情,因为每个点要在两座桥之间走的距离取最小值,我们很难确定当前桥对所有人的影响。这时候感觉无路可走了,但注意到我们之前都是从桥来考虑对人的影响,我们不妨反过来,解决刚刚那个困难的问题,两座桥中人会选择哪座桥。手玩一些例子可以发现,貌似假如将每个人的 (S_i, T_i) 看作是一条线段,那么他回选择距离这条线段中点近的那座桥。证明分两个端点和两座桥的位置关系即可。

    有了上面这个人选桥的最优方案,不难发现最终所有人(按中点从小到大排序)选桥的方案一定是左边的人选一座桥,右边的人选另一座桥。那么左右两边桥的位置我们就可以使用 (K = 1) 的方法来求解了。但是中间的分界点还是难以确定,因此我们还需枚举中间的分界点。那么我们怎样快速计算答案呢?假设这 (2n) 个点的坐标分别为 (x_1 cdots x_{2n}),桥选在 (d),那么答案的计算式应该是:

    [sumlimits_{x_i > d} x_i - d + sumlimits_{x_i < d} d - x_i ]

    于是我们需要一个能查询一组数的中位数,查询比某个数小的数有多少个,以及这些数的权值为多少的数据结构,权值线段树是一个好的选择。

    #include<bits/stdc++.h>
    using namespace std;
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    #define mid (l + r) / 2
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    typedef long long ll;
    const int N = 200000 + 5;
    const ll inf = 10000000000000000;
    ll ans; 
    char P[2], Q[2];
    int n, m, K, S, T, d[N];
    struct tree{
    	int cnt[N << 2]; ll sum[N << 2];
    	void update(int p, int l, int r, int x, int y, int k){
    		if(l >= x && r <= y){ sum[p] += 1ll * d[l] * k, cnt[p] += k; return;}
    		if(mid >= x) update(ls, l, mid, x, y, k);
    		if(mid < y) update(rs, mid + 1, r, x, y, k);
    		sum[p] = sum[ls] + sum[rs], cnt[p] = cnt[ls] + cnt[rs];
    	}
    	ll query(int p, int l, int r, int k){
    		if(l == r) return 1ll * k * d[l];
    		if(cnt[ls] >= k) return query(ls, l, mid, k); // 开始这里复制 find 时忘记改了,调了一年
    		else return sum[ls] + query(rs, mid + 1, r, k - cnt[ls]);
    	}
    	int find(int p, int l, int r, int k){
    		if(l == r) return d[l];
    		if(cnt[ls] >= k) return find(ls, l, mid, k);
    		else return find(rs, mid + 1, r, k - cnt[ls]);
    	}
    }T1, T2;
    struct node{
    	int l, r;
    }a[N], b[N];
    int read(){
    	char c; int x = 0, f = 1;
    	c = getchar();
    	while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    namespace S1{
    	int cnt = 0, c[N];
    	void solve(){
    		rep(i, 1, n) c[++cnt] = a[i].l, c[++cnt] = a[i].r;
    		sort(c + 1, c + cnt + 1);
    		rep(i, 1, cnt) ans += abs(c[i] - c[n]);
    		printf("%lld", ans);
    	}
    }
    namespace S2{
    	int tot = 0; long long tmp = inf, res = 0, Sl = 0, Sr = 0;
    	bool cmp(node a, node b){
    		return a.l + a.r < b.l + b.r;
    	}
    	void solve(){
    		sort(a + 1, a + n + 1, cmp);
    		rep(i, 1, n) d[++tot] = a[i].l, d[++tot] = a[i].r;
    		sort(d + 1, d + tot + 1);
    		tot = unique(d + 1, d + tot + 1) - d - 1;
    		rep(i, 1, n){
    			b[i].l = lower_bound(d + 1, d + tot + 1, a[i].l) - d;
    			b[i].r = lower_bound(d + 1, d + tot + 1, a[i].r) - d;
    		}
    		rep(i, 1, n) Sr += a[i].l, Sr += a[i].r;
    		rep(i, 1, n) T2.update(1, 1, tot, b[i].l, b[i].l, 1), T2.update(1, 1, tot, b[i].r, b[i].r, 1);
    		rep(i, 1, n){
    			T1.update(1, 1, tot, b[i].l, b[i].l, 1), T1.update(1, 1, tot, b[i].r, b[i].r, 1);
    			T2.update(1, 1, tot, b[i].l, b[i].l, -1), T2.update(1, 1, tot, b[i].r, b[i].r, -1);
    			Sl += a[i].l, Sl += a[i].r, Sr -= a[i].l, Sr -= a[i].r;
    			int P1 = T1.find(1, 1, tot, i), P2 = T2.find(1, 1, tot, n - i);
    			ll S1 = T1.query(1, 1, tot, i), S2 = T2.query(1, 1, tot, n - i);
    			res = 1ll * P1 * i - S1 + Sl - S1 - 1ll * P1 * i;
    			res += 1ll * P2 * (n - i) - S2 + Sr - S2 - 1ll * P2 * (n - i);
    			tmp = min(tmp, res);
    		}
    		printf("%lld", ans + (tmp == inf ? 0 : tmp));
    	}
    }
    int main(){
    	K = read(), m = read(); int num = 0;
    	rep(i, 1, m){
    		scanf("%s", P + 1), S = read(), scanf("%s", Q + 1), T = read();
    		if(P[1] == Q[1]) ans += abs(S - T);
    		else a[++num] = (node){S, T}, ++ans;
    	}
    	n = num;
    	if(K == 1) S1 :: solve();
    	else S2 :: solve();
    	return 0;
    }
    
    GO!
  • 相关阅读:
    lr如何获取当前系统时间戳
    linux创建用户、设置密码、修改用户、删除用户
    Linux下安装load generator步骤及问题解决
    怎么将手动设定的IP变成固定的自动IP.
    Redis与Memcached的区别
    memcached 下载安装
    linux上传下载文件rz,sz
    oracle错误码
    sharepoint 2013 附件控件FileUpload怎样检验是否为图片的方法
    10gocm-&gt;session3-&gt;数据备份与恢复
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13519572.html
Copyright © 2011-2022 走看看