vijos1901 学姐的钱包

44 篇文章 0 订阅
6 篇文章 0 订阅

描述

学姐每次出门逛街都要带恰好M元钱, 不过她今天却忘记带钱包了.
可怜的doc只好自己凑钱给学姐, 但是他口袋里只有一元钱.
好在doc的N位朋友们都特别有钱, 他们答应与doc作一些交换.
其中第i位朋友说:
如果doc有不少于Ri元钱,
doc可以把手上所有的钱都给这位朋友,
并从这位朋友手中换回Vi元钱,
但是这次交换会浪费Ti的时间.
doc希望可以在最短的时间内换到M元钱(其实是可以大于M的, 因为doc可以存私房钱呢), 否则学姐会生气的!

格式

输入格式

输入数据第一行给定T, 表示总的询问次数.
对于每一次询问, 第一行给出两个整数N和M.
之后N行, 每一行给出三个整数Vi, Ri和Ti. (保证Ri<=Vi).

输出格式

对于每一次询问, 首先输出询问的编号, 参见样例输出.
之后输出最小需要的时间, 如果不可能完成目标, 则输出-1.

样例1

样例输入1[复制]

3
5 9
5 1 1
10 4 10
8 1 10
11 6 1
7 3 8
4 5
2 1 1
3 2 1
4 3 1
8 4 1
3 10
5 1 3
8 2 5
10 9 2

样例输出1[复制]

Case #1: 10
Case #2: 4
Case #3: -1

限制

对于40%的数据
N <= 1500.

对于100%的数据
T <= 5
1 <= N <= 100000.
1 <= M <= 1000000000.
1 <= Ri <= Vi <= 1000000000.
1 <= Ti <= 1000000000.




因为大于等于r[i]的钱可以换v[i],类似从一个区间转化到一个点,所以这道题我们可以选择逆推,这样就可以将每次修改的操作转化为用点的相关值来修改区间的相关值。(这里解释并不是很清楚…大家自己意会吧…)

用f[i]表示从大于等于m的点到i的最短时间。

首先按照v[i]从大到小排序。(这是为了保证每次更新的正确性)

再依次用f[v[i]]+t[i]更新[r[i],INF]的所有f值。显然f具有单调不增性,所以只需要更新[r[i],v[i]-1]的所有f值即可。

那么问题就转化为区间修改和单点查询,用线段树解决。

因为数据范围很大,所以要用到离散化

注:这道题用到的逆推排序思路很不错。





#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cmath>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define LL long long
#define pa pair<int,int>
#define MAXN 500010
#define INF 1000000000000000LL
using namespace std;
int tt,n,m,a[MAXN],b[MAXN],c[MAXN];
LL ti[MAXN];
struct tree_type
{
	int l,r;
	LL mi,tag;
}t[MAXN*4];
int read()
{
	int ret=0,flag=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') flag=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
	return ret*flag;
}
bool cmp1(int x,int y)
{
	return a[x]<a[y];
}
bool cmp2(int x,int y)
{
	return b[x]>b[y];
}
void build(int k,int l,int r)
{
	t[k].l=l;
	t[k].r=r;
	int mid=(l+r)>>1;
	t[k].mi=INF;
	t[k].tag=INF;
	if (l==r) return;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
void update(int k,LL z)
{
	t[k].tag=min(t[k].tag,z);
	t[k].mi=min(t[k].mi,z);
}
void pushdown(int k)
{
	if (t[k].tag==INF) return;
	update(k<<1,t[k].tag);
	update(k<<1|1,t[k].tag);
	t[k].tag=INF;
}
void pushup(int k)
{
	t[k].mi=min(t[k<<1].mi,t[k<<1|1].mi);
}
void change(int k,int x,int y,LL z)
{
	int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
	if (l==x&&r==y)
	{
		update(k,z);
		return;
	}
	pushdown(k);
	if (y<=mid) change(k<<1,x,y,z);
	else if (x>mid) change(k<<1|1,x,y,z);
	else change(k<<1,x,mid,z),change(k<<1|1,mid+1,y,z);
	pushup(k);
}
LL query(int k,int x)
{
	int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
	if (l==r) return t[k].mi;
	pushdown(k);
	if (x<=mid) return query(k<<1,x);
	else return query(k<<1|1,x);
}
int main()
{
	tt=read();
	F(ii,1,tt)
	{
		n=read();m=read();
		F(i,1,n) a[i]=read(),a[i+n]=read(),ti[i]=read();
		a[2*n+1]=m;a[2*n+2]=1;
		F(i,1,2*n+2) c[i]=i;
		sort(c+1,c+2*n+3,cmp1);
		int tot=0;
		F(i,1,2*n+2)
		{
			if (i==1||a[c[i]]!=a[c[i-1]]) tot++;
			b[c[i]]=tot;
		}
		F(i,1,n) c[i]=i;
		sort(c+1,c+n+1,cmp2);
		build(1,1,tot);
		change(1,b[2*n+1],tot,0);
		F(i,1,n)
		{
			LL x=query(1,b[c[i]]);
			if (x==INF) continue;
			if (b[c[i]]>b[n+c[i]]) change(1,b[n+c[i]],b[c[i]]-1,x+ti[c[i]]);
		}
		LL ans=query(1,b[2*n+2]);
		printf("Case #%d: ",ii);
		if (ans==INF) printf("-1\n");
		else printf("%lld\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值