【贪心】环球巡演

【题目描述】
从前,全球共有N座城市。音乐家YXQ准备举行环球演出。
演出一共有M场。巡演计划可以用一个长度为M的序列表示:a1,a2,…,aM(ai≠ai+1)。YXQ将会按顺序依次到达这些城市并进行演出,注意他可能会在一个城市演出多次。
由于路途遥远,YXQ选择乘机旅行。已知现在共有K种航班,每种航班i花费ci,从xi飞往yi,并且每种航班还有一个属性O或R,如果是O,则表示机票为单程机票;如果是R,则表示机票为往返机票,YXQ可以先从xi飞往yi,然后在之后的任意时刻免费从yi返程xi,当然,他也可以选择不使用返程机票。
由于YXQ懒得走路,所以他要求从ai去往ai+1时必须乘坐直达航班,也就是必须从ai直达ai+1。
现在,你作为YXQ的得力助手,需要帮他规划行程,使得乘坐航班的总花费最小。

【输入】
第一行包含两个正整数n,d(2<=n,d<=300000),分别表示城市的个数和巡演的次数。
第二行包含d个正整数a1,a2,…,ad(1<=ai<=n,ai!=ai+1,a1=ad),依次表示巡演计划中每一场所在的城市。
接下来一行包含一个正整数m(3<=m<=300000),表示机票的种类数。
接下来m行,每行首先是两个正整数si,di(1<=si,di<=n,si!=di),分别表示起点与终点;接下来一个字符ti,表示机票的类型,其中“O”表示单程票,“R”表示双程票;最后是一个正整数pi(1<=pi<=109),表示票价。

【输出】
输出一行一个整数,即完成巡演所需支付的最小机票总费用。

【样例输入】
2 5
1 2 1 2 1
4
1 2 R 6
1 2 O 3
2 1 O 3
1 2 R 5
【样例输出】
10

【思路】

考场上本来以为是一道很难的图论,直到我看见 a i a_{i} ai必须直达 a i + 1 a_{i+1} ai+1。我第一个想法是状压dp,把双程票是否买过压缩了,但是这显然违背了数据规模。再加以分析发现,状压dp之所以不可行,是因为其状态冗余,某一点购买了多程票只在下一次到这条边时才有影响,中途携带该信息显然多余。所以我就打算把各条边按先后顺序分开处理,换句话说,就是把巡演路线拆成若干条链,例如:
1-2-3-2-1-2-3-2-1可以拆为:
1-2-1-2-1
2-3-2-3-2

显然,这样处理后同类边的相对顺序并没有发生改变,也就不会影响答案。
拆链的具体实现:
对于一条边a-b,我们考虑如何把它加入到现有的链中。显然,当前它可能可以接在多条链的后面。因此,我们就用链表将所有合法的链放入a-b的可选集合。加入a-b以后,将该链从a-b的可选集合中删去,然后由于该链末尾变为b,所以将该链加入b-a的可选集合中即可。对于每条链,因为是两个节点交替形成的链,我们只需要保存其长度和起始边即可。
贪心:
拆链以后,我们依次在每条链上贪心。而此时贪心标准就显得很简单了。记val1[pos]为从pos边出发单程票的代价,val2[pos]为从pos边出发的双程票的代价,记边a-b为pos,边b-a为prepos。那么我们可以先进行下面这个操作:
v a l 1 [ p o s ] = m i n ( v a l 1 [ p o s ] , v a l 2 [ p o s ] ) val1[pos]=min(val1[pos],val2[pos]) val1[pos]=min(val1[pos],val2[pos])
v a l 2 [ p o s ] = m i n ( v a l 2 [ p o s ] , v a l 1 [ p o s ] + v a l 1 [ p r e p o s ] ) val2[pos]=min(val2[pos],val1[pos]+val1[prepos]) val2[pos]=min(val2[pos],val1[pos]+val1[prepos])
(对prepos同理)
然后对于每一条链,我们一共需要考虑三种情况:
1.全部走单程票
2.全部走a-b双程票(根据链长奇偶性判断是否多走一步单程票)
3.先走一步a-b,全部走b-a双程票(根据链长奇偶性判断是否需要多走一步)
对三种情况取最小值,就是这一条链对于答案的贡献了。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
#define int long long
using namespace std;
const int N=3e5+5;
tr1::unordered_map<long long,int>val1;//单程最小
tr1::unordered_map<long long,int>val2;//双程最小 
tr1::unordered_map<long long,int>id;
int f[N],bcnt=0,nxp[N<<1|1];
struct node{
	long long u,v;
}e[N<<1|1];
inline void add(long long u,long long v)
{
	e[++bcnt].u=u;
	e[bcnt].v=v;
	nxp[bcnt]=f[u];
	f[u]=bcnt;
}
inline long long idx(int x,int y){return 1ll*x*N+y;}
inline long long idx2(long long pos)
{
	int b=pos%N;
	int a=(pos-b)/N;
	return idx(b,a);
}
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
int n,m,a,b,c;
char op;
long long pos=0,prepos=0,ans=0;
int trip[N],tcnt=0,cnt=0;
int lian[N],all=0;
long long first[N];
signed main()
{
	n=red();tcnt=red();
	for(int re i=1;i<=tcnt;i++)trip[i]=red();m=red();
	for(int re i=1;i<=m;i++)
	{
		a=red(),b=red(),op=getchar(),c=red();
		pos=idx(a,b);
		if(op=='O')val1[pos]=(val1[pos]==0?c:min(val1[pos],c));
		else
		{
			val2[pos]=(val2[pos]==0?c:min(val2[pos],c));
			val1[pos]=(val1[pos]==0?c:min(val1[pos],c));
		}
	}
	for(int re i=1;i<tcnt;i++)
	{
		a=trip[i],b=trip[i+1];
		pos=idx(a,b);
		if(!id[pos])id[pos]=++cnt;
	}
	for(int re i=1;i<tcnt;i++)
	{
		a=trip[i],b=trip[i+1];
		pos=idx(a,b);prepos=idx(b,a);
		int u=id[pos];
		if(f[u])
		{
			int v=e[f[u]].v;
			lian[v]++;
			f[u]=nxp[f[u]];
			add(id[prepos],v);
		}
		else
		{
			lian[++all]++;
			first[all]=pos;
			add(id[prepos],all);
		}
	}
	for(int re i=1;i<=all;i++)
	{
		
		pos=first[i];
		prepos=idx2(pos);
		long long dt=1e18;
		if(!val1[pos])val1[pos]=2e9;
		if(!val1[prepos])val1[prepos]=2e9;
		if(!val2[pos])val2[pos]=2e9;
		if(!val2[prepos])val2[prepos]=2e9;
		val2[pos]=min(val2[pos],val1[pos]+val1[prepos]);
		val2[prepos]=min(val2[prepos],val1[pos]+val1[prepos]);
		dt=min(dt,(long long)((lian[i]+1)/2)*val1[pos]+(lian[i]/2)*val1[prepos]);
		dt=min(dt,(long long)((lian[i]/2)*val2[pos]+(lian[i]&1)*val1[pos]));
		if(lian[i]&1)dt=min(dt,(long long)(val1[pos]+(lian[i]/2)*val2[prepos]));
		else dt=min(dt,(long long)(val2[pos]+(lian[i]/2-1)*val2[prepos]));
		ans+=dt;
	}
	cout<<ans<<"\n";
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值