zoukankan      html  css  js  c++  java
  • P7294-[USACO21JAN]Minimum Cost Paths P【单调栈】

    正题

    题目链接:https://www.luogu.com.cn/problem/P7294


    题目大意

    (n imes m)的网格,当你在((x,y))时你有两种选择

    1. 花费(x^2)的代价向右移动
    2. 花费(c_y)的代价向下移动

    (q)次询问((1,1))走到((x,y))的最小代价。

    (1leq nleq 10^9,1leq m,qleq 2 imes 10^5)


    解题思路

    假设我们开始都是直接走最上面向右的路(也就是代价都是(1^2))。

    然后考虑在某个位置((x,y))要走到((n,m))时向下走会产生的贡献为(c_y+(m-y) imes (2x+1))(后面要抬(m-y)个横着走,然后从(x^2)((x+1)^2)要到加(2x+1))。

    然后拆一下就是(c_y+m-y+2xm-2xy)。发现斜率(-2y)是按照(y)递增递减的,而且我们要求选出的若干个(c_y)(y)一定要递增,但是这样的话我们选出来的一定是递增的所以我们只需要考虑对于每个(x)选择一个(y)使得最小化(c_y+(m-y) imes (2x+1))

    按照询问排序,一个一个加入新的(c_y),按照斜率维护一个上凸壳,然后在凸壳上面二分就好了。

    时间复杂度(O(m+qlog m))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=2e5+10;
    struct node{
    	ll n,m,id;
    }q[N];
    ll n,m,t,top,b[N],k[N],s[N],sum[N],l[N],r[N],ans[N];
    ll calc(ll i,ll j)//i<j
    {return (b[j]-b[i]+k[i]-k[j]-1)/(k[i]-k[j]);}
    ll cap(ll i,ll j){
    	ll x=calc(i,j);
    	if(x<=l[i])return 1;
    	return 0;
    }
    ll getf(ll x,ll l,ll r)
    {return (r+l)*(r-l+1)/2*k[x]+b[x]*(r-l+1);}
    bool cmp(node x,node y)
    {return x.m<y.m;}
    signed main()
    {
    //	freopen("data.in","r",stdin);
    //	freopen("data.out","w",stdout);
    	scanf("%lld%lld",&n,&m);
    	for(ll i=1;i<=m;i++){
    		scanf("%lld",&b[i]);
    		b[i]=b[i]-i;k[i]=2*(-i);
    	}
    	scanf("%lld",&t);
    	for(ll i=1;i<=t;i++){
    		scanf("%lld%lld",&q[i].n,&q[i].m);
    		q[i].id=i;
    	}
    	sort(q+1,q+1+t,cmp);
    	for(ll i=1,z=1;i<=m;i++){
    		r[i]=n;
    		while(top>0&&cap(s[top],i))top--;
    		if(top)l[i]=max(calc(s[top],i),1ll);else l[i]=1;
    		if(l[i]<=r[i]){
    			r[s[top]]=l[i]-1;s[++top]=i;
    			sum[top-1]=((top>1)?sum[top-2]:0)+getf(s[top-1],l[s[top-1]],r[s[top-1]]);
    			sum[top]=sum[top-1]+getf(i,l[i],r[i]);
    		}	
    		while(z<=t&&q[z].m<=i){
    			ll qn=q[z].n-1,L=1,R=top;
    			while(L<=R){
    				ll mid=(L+R)>>1;
    				if(r[s[mid]]<qn)L=mid+1;
    				else R=mid-1;
    			}
    			ans[q[z].id]=sum[L-1]+getf(s[L],l[s[L]],qn)+(qn+1)*qn*i+i*qn+i-1;
    			z++;
    		}
    	}
    	for(ll i=1;i<=t;i++)
    		printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    php echo return exit 区别
    Ubuntu下添加开机启动项的2种方法
    在php中定义常量时,const与define的区别
    yii分页操作
    ubuntu14.04设置静态ip
    Yii IIS8下使用伪静态【Url Rewrite】去掉index.php
    java 第三方库
    IDEA插件
    springboot扫描通用的依赖模块
    idea注册
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15152175.html
Copyright © 2011-2022 走看看