蓝桥杯2023年第十四届省赛真题-小蓝的旅行计划(线段树+优先队列)

原题链接: 小蓝的旅行计划


若没有油箱的限制,仅用优先队列即可。将所有加油站扔到堆里,贪心得在油价最少的加油站加油。

但若有限制,在一个加油站加油后,之后到达的加油站的到达油量也要更新,能加的油量也要更新,故采用线段树or树状数组维护区间到达加油站的油量的最大值。即,若要到达加油站 i i i,选择在之前的加油站 j j j加油,能加的油量为: m i n ( l i m [ j ] , m a x V o l u m n − r e s t [ j , i − 1 ] ) min(lim[j],maxVolumn-rest[j,i-1]) min(lim[j],maxVolumnrest[j,i1]).

其中, l i m [ j ] lim[j] lim[j]表示加油站 j j j 的加油限制, m a x V o l u m n maxVolumn maxVolumn为油箱容量, r e s t [ j , i − 1 ] rest[j,i-1] rest[j,i1]表示在加油站 j j j到加油站 i − 1 i-1 i1中各到达油量的最大值,保证加油后油量不超过最大油量。

复杂度 O ( n ∗ l o g ( n ) ) O(n*log(n)) O(nlog(n)),常数较大。

import java.io.*;
import java.util.*;
  
public class Main{
    static int maxn = 200005,n,m,inf=(int)1e9;
    static long INF = (long)2e18,ans = 0,mod = (int)1e9+7;
    static Scanner sc = new Scanner (System.in);
    static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st  =new StreamTokenizer(bf);
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[]args) throws IOException{
        int T = 1;
        while(T-->0) solve();
        pw.flush();
    }
    static final int I() throws IOException {
        st.nextToken();
        return (int)st.nval;
    }
    
    static class gas implements Comparable<gas>{
		long c;
		int id;
		public gas(int i,long a) {
			c=a;id=i;
		}
		@Override
		public int compareTo(gas o) {
			// TODO Auto-generated method stub
			return this.c -o.c>0?1:-1;
		}
		
	}
    
    static int rest[] = new int [maxn<<2];
    static int k[] = new int [maxn<<2];
    static int dis[] = new int [maxn];
	static int lim[] = new int [maxn];
	static int cost[] = new int [maxn];
    static int vol = 0;
    
    static void build(int i,int l,int r) {
    	if(l == r) {
    		rest[i]=0; //到达时油量
    		return ;
    	}
    	int mid = (l+r)/2;
    	build(i<<1,l,mid);build(i<<1|1,mid+1,r);
    	up(i);
    }
    
    static void up(int i) {
    	rest[i] = Math.max(rest[i<<1],rest[i<<1|1]);
    }
    
    static void pd(int i) {
    	if(k[i]!=0) {
    		k[i<<1] += k[i]; k[i<<1|1] += k[i];
    		rest[i<<1] += k[i]; rest[i<<1|1] +=k[i];
    		k[i] = 0;
    	}
    }
    
    static int query(int i,int l,int r,int ll,int rr) {
    	if(ll<=l && r <=rr) return rest[i];
    	pd(i);
    	int res = 0;
    	int mid = (l+r)/2;
    	if(mid >= ll) res = Math.max(res,query(i<<1,l,mid,ll,rr));
    	if(mid < rr) res = Math.max(res,query(i<<1|1,mid+1,r,ll,rr));
    	up(i);
    	return res;
    }
	
    static void add(int i,int l,int r,int ll,int rr,int v) {
    	if(ll<=l && r <=rr) {
    		rest[i]+=v;k[i]+=v;
    		return;
    	}
    	pd(i);
    	int mid = (l+r)/2;
    	if(mid >= ll) add(i<<1,l,mid,ll,rr,v);
    	if(mid < rr) add(i<<1|1,mid+1,r,ll,rr,v);
    	up(i);
    }
    
	static void solve() throws IOException{
		n = I();m = I();
		vol = m;
		PriorityQueue<gas> q = new PriorityQueue<>();
		for(int i=1;i<=n;i++) {
			dis[i]=I();cost[i]=I();lim[i]=I();
		}
		build(1,1,n);
		for(int i=1;i<=n;i++) {
			vol -= dis[i];
			while(vol<0) {
				if(q.isEmpty()) {
					pw.println(-1);return;
				}
				gas a = q.poll();
				int cnt = Math.min(m-query(1,1,n,a.id,i-1), lim[a.id]);
				if(cnt<=0) continue;
				if(cnt <= -vol) {
					ans += a.c *cnt;
					vol += cnt;
					lim[a.id] = 0;
					add(1,1,n,a.id,i-1,cnt);
				}
				else {
					ans += a.c *(-vol);
					lim[a.id] = cnt + vol;
					add(1,1,n,a.id,i-1,-vol);
					q.add(new gas(a.id,a.c));
					vol=0;
				}
			}
			if(vol>0) add(1,1,n,i,i,vol);
			lim[i] = Math.min(lim[i], m-vol);
			q.add(new gas(i,cost[i]));
		}
		pw.println(ans);
	}
}

小蓝继续努力吧。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值