0%

[CEOI2022] Measures

模拟赛题。

感觉考场上随便推一下应该就会做了,但是我就是坐那干瞪了一整场。

那应该还是我菜了

题目链接

题意

现在有一根数轴,这个数轴上初始有 个人,其中第 个人的坐标为

每一个人都可以以每秒 单位长度的速度进行移动。

你需要求出,最优情况下,需要多少时间让他们来回走动,使得两两距离都 ,其中 为输入给定的数字。

特别地,本题有 次插入操作,第 次操作插入一个在 的人,你需要在每一次插入操作后,输出上述问题的答案。

题解

感觉是一个小结论题,但是结论稍微推一下就能推出来。

假设只有单组询问,我们首先将所有人按照初始位置排序,他们的最终顺序显然和初始顺序一样,

即不可能有两人相对方向交换。

设第 个人初始位置为 ,最终位置是 ,时间(即答案)为

首先显然,对于任意两个人 ,会有

比较显然,因为这两个人中间恰有 个间隔,每一个间隔长度都至少是

把上面的式子稍化一下,就可以得到

其次,我们还有每一个人最终位置的限制,即

我们发现上面一条,两个人在比较时,采用的是 ,所以我们把下面的限制也改一下。

其实整个问题其实在问,选择一个最小的 ,使得每一个人 能选择一个数字 ,使得

假如我们已经确定了 是多少,能否知道这个 是否合法呢?

因为有了 递增这个限制,我们肯定是让前面的 越小越好。

所以从贪心的角度考虑,我们选择的 一定有

我们只需要检查一下是否有所有 就好了。

不难发现,上述检验是充要的。

然后呢?

再仔细观察,我们会发现有一个更简单的式子:

很好证明,对上面 的式子进行归纳即可。

合法的充要条件,其实就是对于任意的 ,有

所以,

我们要求的最小的 其实就是

线段树进行统计即可。

时间复杂度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
using ll=long long;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
struct node
{
int id;
int x;
}a[400001];
ll num[1600001];
ll ma[1600001];
ll mi[1600001];
ll c[1600001];
int t[400001];
int w[400001];
int x[400001];
int n,m,d;
int cnt;
void build(int p,int pl,int pr)
{
num[p]=-inf,ma[p]=-inf,mi[p]=inf;
if(pl==pr) return ;
int mid=(pl+pr)>>1;
build(p*2,pl,mid);
build(p*2|1,mid+1,pr);
}
inline void push_up(int p)
{
c[p]=c[p*2]+c[p*2|1];
ma[p]=max(ma[p*2],ma[p*2|1]-d*c[p*2]);
mi[p]=min(mi[p*2],mi[p*2|1]-d*c[p*2]);
num[p]=max(max(num[p*2],num[p*2|1]),ma[p*2]-(mi[p*2|1]-c[p*2]*d));
}
void change(int p,int pl,int pr,int x,int y)
{
if(pl==pr)
{
mi[p]=ma[p]=y;
c[p]=1,num[p]=0;
return ;
}
int mid=(pl+pr)>>1;
if(x<=mid) change(p*2,pl,mid,x,y);
else change(p*2|1,mid+1,pr,x,y);
push_up(p);
}
inline void solve()
{
build(1,1,n+m);
for(int i=1;i<=n+m;i++)
{
change(1,1,n+m,w[i],x[i]);

if(i>n)
{
ll ans=max(num[1],0ll);
if(num[1]&1) printf("%lld.5 ",ans/2);
else printf("%lld ",ans/2);
}
}
}
int main()
{
n=read(),m=read(),d=read();
for(int i=1;i<=n+m;i++)
{
a[i].x=x[i]=read();
a[i].id=i;
}
sort(a+1,a+n+m+1,[](node a,node b){ return a.x<b.x;});
for(int i=1;i<=n+m;i++) w[a[i].id]=i;
solve();
return 0;
}

感谢观看!