题目大意:
题目链接:https://jzoj.net/junior/#main/show/2320
思路:
被C组的题目血虐
q
w
q
qwq
qwq
感觉这道题还是不止C组难度的啊。拿到B组去应该也没几个人可以做出来。害得我一下午没有改A组题
把每一个花看成一个点。
首先由一个很显然的性质。如果我们已经取好了
[
1
,
x
]
[1,x]
[1,x]的所有花,那么我们下次一定会取一个连续区间
[
x
+
1
,
y
]
[x+1,y]
[x+1,y]的所有花。也就是说,不会存在某一个时刻我们取的花是不连续的。任意时刻取的花一定是一个左端点为1的连续区间。
所以有一个相对明显的
d
p
dp
dp:设
f
[
i
]
f[i]
f[i]表示取完
[
1
,
i
]
[1,i]
[1,i]所有的花并且走到点
i
i
i所需要的最小时间。
那么就可以枚举这一次取的花的区间
[
j
,
i
]
[j,i]
[j,i],那么我们需要求的就是从第
j
j
j朵花开始,在第
i
i
i朵花结束,并且取完
[
j
,
i
]
[j,i]
[j,i]所有花的最少花费时间。
那么如果我们第二次经过一朵花时,这朵花已经种下了,那么就不需要花费任何的时间等待。所以这样子所花费的时间如下图
其中红色线条表示采摘其中任意一朵花花费的时间。
我们发现,从第一次经过这个点,准备放下花,到我我们最后一次经过这个点,采摘这朵花,所需要的时间(红色线条)正好为
2
(
a
[
i
]
−
a
[
j
]
)
2(a[i]-a[j])
2(a[i]−a[j])!
所以无论我们采摘奶一个花,我们花费的时间是与这朵花的位置无关的。
但是如果一朵花所需的等待时间
m
m
m是大于
2
(
a
[
i
]
−
a
[
j
]
)
2(a[i]-a[j])
2(a[i]−a[j])的话,那么我们就得等待
m
−
2
(
a
[
i
]
−
a
[
j
]
)
m-2(a[i]-a[j])
m−2(a[i]−a[j])秒。也就是说,采摘
[
l
,
r
]
[l,r]
[l,r]的花,我们需要花费时间为
m
a
x
(
a
[
r
]
−
a
[
l
]
,
m
)
max(a[r]-a[l],m)
max(a[r]−a[l],m)。
所以这样我们就可以轻松的设计出一个
O
(
n
2
)
O(n^2)
O(n2)的
d
p
dp
dp。
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
+
a
[
i
]
−
a
[
j
−
1
]
+
m
a
x
(
2
×
(
a
[
i
]
−
a
[
j
]
)
,
m
)
)
f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+max(2\times (a[i]-a[j]),m))
f[i]=min(f[i],f[j−1]+a[i]−a[j−1]+max(2×(a[i]−a[j]),m))
其中
a
[
i
]
−
a
[
j
−
1
]
a[i]-a[j-1]
a[i]−a[j−1]是从上一个结束的花
(
j
−
1
)
(j-1)
(j−1)到
i
i
i的时间。
那么这样就可以获得
50
p
t
s
50pts
50pts的高分。
考虑如何优化这个
d
p
dp
dp。我们发现这个
d
p
dp
dp最关键的地方就是
m
a
x
max
max,如果我们知道谁大谁小就可以把这个
m
a
x
max
max省掉了。
也就是说
当
2
×
(
a
[
i
]
−
a
[
j
]
)
>
m
2\times (a[i]-a[j])>m
2×(a[i]−a[j])>m时,原方程为
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
+
a
[
i
]
−
a
[
j
−
1
]
+
2
×
(
a
[
i
]
−
a
[
j
]
)
)
f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+2\times (a[i]-a[j]))
f[i]=min(f[i],f[j−1]+a[i]−a[j−1]+2×(a[i]−a[j]))
化简
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
−
a
[
j
−
1
]
−
2
a
[
j
]
+
3
a
[
i
]
)
f[i]=min(f[i],f[j-1]-a[j-1]-2a[j]+3a[i])
f[i]=min(f[i],f[j−1]−a[j−1]−2a[j]+3a[i])
同理,当
m
>
2
×
(
a
[
i
]
−
a
[
j
]
)
m>2\times (a[i]-a[j])
m>2×(a[i]−a[j]),原方程为
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
+
a
[
i
]
−
a
[
j
−
1
]
+
m
)
f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+m)
f[i]=min(f[i],f[j−1]+a[i]−a[j−1]+m)
我们发现,在枚举
i
i
i时,
3
a
[
i
]
3a[i]
3a[i]和
a
[
i
]
a[i]
a[i]都是固定的,所以我们就只需要分别维护
f
[
j
−
1
]
−
a
[
j
−
1
]
−
2
a
[
j
]
f[j-1]-a[j-1]-2a[j]
f[j−1]−a[j−1]−2a[j]和
f
[
j
−
1
]
+
a
[
i
]
−
a
[
j
−
1
]
f[j-1]+a[i]-a[j-1]
f[j−1]+a[i]−a[j−1]的最小值即可。
同时,容易发现在
i
i
i递增的过程中,满足
2
(
a
[
i
]
−
a
[
j
]
)
>
m
2(a[i]-a[j])>m
2(a[i]−a[j])>m的花
j
j
j一定是一个区间
[
1
,
x
]
[1,x]
[1,x]且
x
x
x是单调不降的。所以我们就只要在把
i
i
i每往后挪一位时,利用单调不降的性质,维护好对应的有哪一些花是满足
2
(
a
[
i
]
−
a
[
j
]
)
>
m
2(a[i]-a[j])>m
2(a[i]−a[j])>m,又有哪一些花是满足
2
(
a
[
i
]
−
a
[
j
]
)
<
m
2(a[i]-a[j])<m
2(a[i]−a[j])<m的,然后分别用两个堆维护一下它们的最小值,然后取
m
i
n
min
min即可。
注意堆需要支持删除任意数字,那么就只能手写可删除堆了。
同时花的下标达到了
1
0
9
10^9
109级别,所以需要开两个
m
a
p
map
map,别分记录字典序和一个数出现的个数。这样复杂度又增加了
log
\log
log。
细节还是很多了,调了我一个下午+晚上。
时间复杂度
O
(
n
log
2
n
)
O(n\log^2 n)
O(nlog2n)。比较卡,最好吸氧(狗头保命
代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N=100010,Inf=1e18;
ll n,t,m,j,a[N],f[N];
struct Heap
{
ll a[N],tot;
map<ll,ll> way,cnt;
//ll cnt[N];
ll size()
{
return tot;
}
ll top()
{
if (!tot) return Inf;
return a[1];
}
void push(ll x)
{
cnt[x]++;
if (cnt[x]>1) return;
a[++tot]=x;
ll fa=(tot>>1),k=tot;
way[x]=way[a[fa]]*2+(tot&1);
while (a[fa]>a[k]&&fa)
{
swap(way[a[k]],way[a[fa]]);
swap(a[k],a[fa]);
k=fa; fa>>=1;
}
}
void pop(ll x)
{
cnt[a[x]]--;
if (cnt[a[x]]) return;
swap(way[a[x]],way[a[tot]]);
swap(a[x],a[tot]);
way[a[tot]]=0;
a[tot]=0;
tot--;
ll son=(x<<1);
while ((a[son]<a[x]&&son<=tot)||(a[son+1]<a[x]&&son+1<=tot))
{
if (a[son+1]<a[son]&&son+1<=tot) son++;
swap(way[a[x]],way[a[son]]);
swap(a[x],a[son]);
x=son; son<<=1;
}
}
}q1,q2;
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld%lld%lld",&n,&t,&m); n++;
for (ll i=2;i<=n;i++)
scanf("%lld",&a[i]);
f[1]=0; j=2;
for (ll i=2;i<=n;i++)
{
q2.push(f[i-1]-a[i-1]+m);
for (j<=i;2LL*(a[i]-a[j])>m;j++)
{
q2.pop(q2.way[f[j-1]-a[j-1]+m]);
q1.push(f[j-1]-a[j-1]-2*a[j]);
}
f[i]=min(q1.top()+3*a[i],q2.top()+a[i]);
//if (i==30) printf("%lld\n",f[i]);
}
printf("%lld",f[n]+t-a[n]);
return 0;
}