zoukankan      html  css  js  c++  java
  • 【luogu P7294】Minimum Cost Paths P(二分)(单调栈)(斜率)

    Minimum Cost Paths P

    题目链接:luogu P7294

    题目大意

    给你一个 n*m 的矩阵,一开始你在 (1,1)。
    如果你在 (x,y),你可以花费 x^2 的费用走到 (x,y+1),也可以花费 cy 的费用走到 (x+1,y)。
    然后多组询问,每次问你要走到 (x,y) 的最小费用。

    思路

    由于 (n) 很大,(m) 比较小,我们考虑离线,然后把询问按 (m) 从小到大排。
    那我们考虑 (m) 多了 (1) 会怎么样,就会要有一个位置从 (m-1) 向下走,然后剩下全部往右走。
    那我们就考虑一开始全部往右走,那贡献就是 (x-1)

    然后考虑在一个地方 ((x,y)) 往下走新多的贡献。(后面的还是全部往右)
    首先是 (c_y),然后剩下的往右走是 ((m-y)*(x+1)^2),然后之前剩下的往右走是 ((m-y)*x^2),相减就是 ((m-y)*(2x+1)),所以新多的就是 (c_y+(m-y)*(2x+1))

    然后拆开移一下:(c_y-y-2xy+2xm+m)
    那右边的 (2xm+m) 都是迟早都要,可以单独拿出来,最后再算贡献。
    那有关的就是 (c_y-y-2xy),然后发现你可以看做是 ((-2y)x+(c_y-y))
    (考虑算最小值)

    那就是一条直线,单调下降。
    然后你要每次选的 (y) 递增,那就是要维护一个上凸壳。(用单调栈维护)
    至于里面的直线,你考虑维护它在 (x) 为那个区间范围的时候最优,然后你询问你就二分出这个直线,前面的提前前缀和统计好,然后这条之间再算一次,就可以了。

    (记得加上之前省略的值)

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    
    struct ask {
    	ll x, y, num;
    	ll ans;
    }qq[200001];
    ll n, m, q, sta[200001], top;
    ll l[200001], r[200001];
    ll c[200001], sum[200001];
    ll k[200001], b[200001];
    
    bool cmp1(ask x, ask y) {
    	return x.y < y.y;
    }
    
    bool cmp2(ask x, ask y) {
    	return x.num < y.num;
    }
    
    ll met(ll x, ll y) {//算出两条直线的交点(向上取整)
    	return (b[y] - b[x] + (k[x] - k[y] - 1)) / (k[x] - k[y]);
    }
    
    bool worse(ll i, ll j) {
    	if (met(i, j) <= l[i]) return 1;
    	return 0;
    }
    
    ll get_line(ll x, ll l, ll r) {//算一整条的贡献
    	return (l + r) * (r - l + 1) / 2 * k[x] + (r - l + 1) * b[x];
    }
    
    int main() {
    	scanf("%lld %lld", &n, &m);
    	for (ll i = 1; i <= m; i++) {
    		scanf("%lld", &c[i]);
    		b[i] = c[i] - i; k[i] = -2 * i;
    	}
    	scanf("%lld", &q);
    	for (ll i = 1; i <= q; i++) {
    		scanf("%d %d", &qq[i].x, &qq[i].y);
    		qq[i].num = i;
    	}
    	sort(qq + 1, qq + q + 1, cmp1);
    	
    	ll now = 1;
    	for (ll i = 1; i <= m; i++) {
    		r[i] = n;
    		while (top && worse(sta[top], i)) top--;
    		if (top) l[i] = max(1ll, met(sta[top], i));
    			else l[i] = 1;
    		if (l[i] <= r[i]) {
    			r[sta[top]] = l[i] - 1;
    			sta[++top] = i;
    			sum[top - 1] = ((top - 1) ? sum[top - 2] : 0) + get_line(sta[top - 1], l[sta[top - 1]], r[sta[top - 1]]);
    			sum[top] = sum[top - 1] + get_line(sta[top], l[sta[top]], r[sta[top]]);
    		}
    		
    		while (now <= q && qq[now].y == i) {
    			ll ans = -1, lll = 0, rr = top;
    			while (lll <= rr) {
    				ll mid = (lll + rr) >> 1;
    				if (r[sta[mid]] >= qq[now].x - 1) rr = mid - 1;
    					else lll = mid + 1, ans = mid;
    			}
    			qq[now].ans = sum[ans] + get_line(sta[ans + 1], l[sta[ans + 1]], qq[now].x - 1);
    			qq[now].ans += (qq[now].x - 1) * (qq[now].x + 1) * qq[now].y;
    			qq[now].ans += qq[now].y - 1;//后面两个是把之前没有算的贡献全部加上
    			now++;
    		}
    	}
    	
    	sort(qq + 1, qq + q + 1, cmp2);
    	for (ll i = 1; i <= q; i++) printf("%lld
    ", qq[i].ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    [hdu4436 str2int]后缀自动机SAM(或后缀数组SA)
    bytedance专题
    LSTM+CRF维特比解码过程
    spark core类梳理
    spark源码阅读---Utils.getCallSite
    python2.7官方文档阅读笔记
    cs224d---词向量表示
    cs231n---强化学习
    cs231n---生成模型
    Spring 2017 Assignments3
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P7294.html
Copyright © 2011-2022 走看看