【AtCoder arc072_f/集训队作业】Dam
所以说思维还是很重要的啊
题意
你有一个初始为空的水坝,每日都会有温度为
ti
,体积为
vi
的水流入。为使总水量不超过常数
L
,你每晚都可以放走一些水,使明日早晨的水可以全部流入。若水的温度不会随时间变化,只会受新流入的水影响,输出
分析
我曾尝试往构造单调队列上想,最后在单调队列上二分答案,但这样在遇到某日温度骤减的情况时难以处理。在观摩了别人的代码后,才意识到这题根本没有那么复杂。
我们断言能把前
i
天的注水在最优策略下等价变换为
1.
这些天的总注水量为
L
。
我们归纳证明:
1.
第一日显然可以
2.
我们要在i-1’天中选择若干天放水。因为
t
最大的同时
再维护单调性:若第
i
日注水水温不为当前最大,由贪心可知,第
根据最优性,将
i′
日的注水混合后的温度即为第
i
<script type="math/tex" id="MathJax-Element-33">i</script>日的答案。
AC代码
#include<cstdio>
#include<algorithm>
using namespace std;
#define re_ return
#define in_ inline
#define op_ operator
#define inc(l, i, r) for(i=l; i<r; ++i)
typedef long long ll;
typedef double db;
struct wat
{
db t; int v;
wat(): t(0), v(0){}
wat(db a, int b): t(a), v(b){}
in_ wat op_+ (wat a)
{re_ v+a.v? wat((t*v+a.t*a.v)/(v+a.v), v+a.v): wat();}
in_ wat& op_+= (wat a)
{re_ *this=*this+a;}
in_ wat op_- (wat a)
{re_ v^a.v? wat((t*v-a.t*a.v)/(v-a.v), v-a.v): wat();}
in_ wat& op_-= (wat a)
{re_ *this=*this-a;}
in_ void wr(char* a)
{printf("(%.6lf, %d)%s", t, v, a);}
};
const int mxn=1<<19;
int n, v0; wat q[mxn];
int main()
{
int s, t; wat a, b;//b维护水坝中的所有水
// a.wr("\n");
scanf("%d%d", &n, &v0);
for(s=t=0; n--;)
{
scanf("%lf%d", &a.t, &a.v);
for(b+=a; b.v>v0;)//放水
if(q[s].v<=b.v-v0) b-=q[s++];
else q[s].v-=b.v-v0, b-=wat(q[s].t, b.v-v0);
printf("%lf\n", b.t);
for(;s<t && q[t-1].t>=a.t; a+=q[--t]);//合并所注水
q[t++]=a;
}
re_ 0;
}