题解 P4968 【逃生之路】
题目地址
题目描述:
还是这群生物ccj,因为上次的打击太草率了,同时被他人侦测到了。在得知自己即将受到黑暗森林打击之后,选择逃离自己的家园。
他们的旅程计划在一条线上,其中,我们约定,一号点是他们的家园,有无限多个生物。每一个星球,都可以向之后连续的
可惜这群生物有一个睿智的头领,他希望最终能有生物到达可以生存的星球,而且消耗的总能源要尽量少。那群生物对这个头领很失望,于是把任务丢给你。
请求出有生物到达可生存星球的情况下,最少的能源消耗是多少?
数据范围:
保证多项式函数二阶导函数在
详细范围参见“标程”
题解部分:
这题显然是一种方案最优化问题,对于方案最优化问题,目前我已知的效率和正确性稳定的算法有以下四大类:暴力,贪心,动态规划,网络流。而这道题目,贪心显然不可以,所以只剩下三种方法,分别讨论。
暴力emmmmmmm,pass!
动态规划,对于这种方案型,一定要有至少一定应该开不下,pass!
只剩下孤苦伶仃的网络流了……所以std的算法也非常显然了。首先我们看到,对于不同的生物转移数量,有不同的能源消耗,这显然就是费用流的常规套路嘛……然后,生物可以按一定顺序后向移动,这显然就是网络流嘛……但是题目说到,这个生物有到达就可以,意味着不用求取最大流。好的,现在问题转化成,最小费用可行流怎么建图。
我们发现有两个难点,一是费用不是正比例函数,二是生物数量有上下界限制。网络流的效率要保证,中间一定不能有多余的计算,否则理论效率的费用流在观看蓝书之后我们考虑最小费用流的一个性质,对于最小费用流,在有重边的情况下,会选择最小价值的一条边跑。一阶差分序列又有一个特性,
对于第二个难点,我们可以直接采用带上下界的可行流的方式建图,因为费用流本身建立于可行流之上,只要可行流求出来,费用就算遍历一遍也能求出来,当然,还要保证最小。那这种图怎么建呢?我们考虑可行流的一个性质,除了源点和汇点,保证每个点流量平衡。假设从根据LG的最小费用最大流模板题这个效率太快了,显然可以过去。
想到这里,你是否会觉得万事大吉了?如果你花了好大心血,写完这些,会发现
再次搜刮脑中关于网络流的知识,我们又会发现,曾经好像网络流会被用作求解欧拉回路!在求解欧拉回路时,我们通过转化,把每个点的入度等于出度转化成流量平衡。所以我们需要再次利用以下这个性质,把图中每个点拆成两个点,建立另外的源点和汇点。从源点向每个点连一条还是根据LG的模板题数据范围这样的效率我们能够接受,所以就能愉快AC啦!
提醒一下,这题的多项式会爆
由于这题建边复杂,在此提供std部分代码。
建边部分保证能看懂,add(u,v,cap,val),其他部分拒绝解释,请自行完成代码!
inline long long pre()
{
register int N=2*n+4,exs=2*N+1,ext=2*N+2;
flow.init(2*N+2);
flow.add(t,s+N,INF,0),flow.add(s,1+N,INF,0),m=5;
for(register int i=2;i<=n;++i)
if(f[i])flow.add(i+n,t+N,INF,0),m+=2;
for(register int i=1;i<=n;++i)
for(register int j=1;j<=jump;++j)
{
if(i+j>n)break;
if(i!=1)flow.add(i+n,i+j+N,INF,0),m+=2;
else flow.add(i,i+j+N,INF,0),m+=2;
}
for(register int i=2;i<=n;++i)
{
for(register int j=1;j<=a[i];++j)
if(j<=b[i])
flow.add(i,ex1+N,1,chafen[j][i]),m+=2;
else
flow.add(i,i+n+N,1,chafen[j][i]),m+=2;
flow.add(ex2,i+n+N,b[i],0),m+=2;
}
for(register int i=1;i<=2*n+4;++i)
flow.add(exs,i,INF,0),flow.add(i,i+N,INF,0),flow.add(i+N,ext,INF,0);
flow.work(exs,ext,1);
for(register int i=2;i<=m;++i,++i)
edge[i].start=flow.extract_edge(i,1),edge[i].end=flow.extract_edge(i,2)-N,edge[i].val=flow.extract_edge(i,3),edge[i].cost=flow.extract_edge(i,4);
for(register int i=3;i<=m;++i,++i)
edge[i].start=flow.extract_edge(i,1)-N,edge[i].end=flow.extract_edge(i,2),edge[i].val=flow.extract_edge(i,3),edge[i].cost=flow.extract_edge(i,4);
return flow.mn_cost();
}
inline long long make()
{
long long ans=pre();
flow.init(2*n+4);
for(register int i=2;i<=m;++i)
flow.force_add(edge[i].start,edge[i].end,edge[i].val,edge[i].cost);
flow.work(ex2,ex1,1);
if(tot_b!=flow.mx_flow())return INF;
flow.work(s,t,0);
return ans+flow.mn_cost();
}
本题预计难度:黑?紫题。
谢谢大家!