《P10438 [JOISC 2024 Day3] 塔楼》解题报告
一蓑烟雨里 春光不觉老
折扇挑珠帘 依稀闻童谣
东风却远送 小径人寥寥
桃花探杯中 谁家女郎笑
—————⌈岁时记⌋
没人发题解,我来水一发。
首先容易想到对于
我们先来考虑
容易知道,我们的图是若干段不施工的区域合并起来的,我们记
那么我们现在想尽可能的少用操作
然后对于询问
这一部分的复杂度是
接下来考虑
显然只有在某个
我们考虑动态规划,记
考虑一个块中的
看上去好像是上面这么一回事,
我们现在卡住了,没有更多很好的想法了,找点性质。
一个段中前
D 个元素,记他为第y 个,满足f_{1\sim y}=x,f_{y+1\sim D}=x+1
考虑证明(证明比较感性,不严谨,看看就好了,不看也可以,直接跳了):
假设
f_y=x,f_{y+1}=x+k(k>1) ,考虑本质上为什么f_y 会比f_{y+1} 少,因为我们1 操作走了太多了,也就是1 操作浪费了太多步数。我们记
g_y 表示y 执行1 的次数,g_{y+1} 为y+1 执行y 的次数,那么f_{y+1}-f_y=k ,也就是说g_{y+1}-g_y\ge D ,而倘若浪费的步数比y+1 的方案多D ,那么实际上我们是可以在中途切换到y+1 的方案,可以看下面这个图。(因为我们想要观察的是g_y,g_{y+1} 的相对性,所以我们钦定g_{y+1}=0 。)我们倒着看,绿色点表示红色要走到这点才能进行二操作,也就是要浪费绿色点和红色点间距离的
1 操作,那么当浪费的操作\ge D 时,就可以碰到y+1 路径上的点了,也就说明按y+1 那样走是可以更新到我们原本y 路径上的点的,同时还能少浪费D 次1 操作。所以是对的。
也就是一个段前
而因为值只差
所以我们现在对于一个段只需要求前
而对于一个段的前
所以实际上实现出来是一个二分套二分的形式,计算是简单的,对于每个段记
然后查询的话我们是可以做到
预处理需要
所以这种情况的时间复杂度为
总时间复杂度为
代码写起来没什么细节。
#include<bits/stdc++.h>
#define Yzl unsigned long long
#define mp make_pair
#define pi pair<LL,LL>
#define fi first
#define se second
#define pb emplace_back
typedef long long LL;
using namespace std;
const Yzl Lty=20120712;
const int MAXN=2e5+10;
const LL inf=1e13;
int n,Q,cnt;
LL D,A,B;
struct ddl {
LL l,r;
}a[MAXN],b[MAXN],c[MAXN];
LL f[MAXN];
void sub1() {
while(Q--) {
LL x; scanf("%lld",&x);
LL l=0,r=cnt+1,mid;
while(l+1<r) { mid=(l+r)/2;
if(c[mid].r<x) l=mid;
else r=mid;
}
if(c[r].l>x||r==cnt+1) puts("-1");
else printf("%lld\n",x*A+f[r]*(B-A*D));
}
}
LL pos[MAXN];
LL erfind(LL x) {
LL l=0,r=cnt+1,mid;
while(l+1<r) { mid=(l+r)/2;
if(c[mid].r<x) l=mid;
else r=mid;
}
if(r==cnt+1||c[r].l>x) x=c[--r].r;
return f[r]+(x-c[r].l)/D+((x-c[r].l)%D>=pos[r]);
}
void sub2() {
f[1]=0; pos[1]=D;
for(int i=2;i<=cnt;++i) {
LL ls=erfind(c[i].l-D)+1; f[i]=ls;
LL l=c[i].l,r=min(c[i].r,c[i].l+D-1)+1,mid;
while(l+1<r) { mid=(l+r)/2;
if(erfind(mid-D)==ls) r=mid;
else l=mid;
} pos[i]=r-c[i].l;
}
while(Q--) {
LL x; scanf("%lld",&x);
LL l=0,r=cnt+1,mid;
while(l+1<r) { mid=(l+r)/2;
if(c[mid].r<x) l=mid;
else r=mid;
}
if(r==cnt+1||c[r].l>x) puts("-1");
else printf("%lld\n",x*A+(f[r]+(x-c[r].l)/D+((x-c[r].l)%D>=pos[r]))*(B-A*D));
}
}
int main () {
scanf("%d%d%lld%lld%lld",&n,&Q,&D,&A,&B);
for(int i=1;i<=n;++i) {
scanf("%lld%lld",&a[i].l,&a[i].r);
} b[++cnt]=(ddl){0,a[1].l-1}; a[n+1].l=inf;
for(int i=1;i<=n;++i) b[++cnt]=(ddl){a[i].r+1,a[i+1].l-1};
int CNT=cnt; c[cnt=1]=b[1]; f[1]=0; int pre=0;
for(int i=1;i<=CNT;++i) {
while(pre<=cnt&&c[pre].r+D<b[i].l) ++pre;
if(pre<=cnt&&c[pre].l+D<=b[i].r) {
c[++cnt]=(ddl){max(b[i].l,c[pre].l+D),b[i].r};
f[cnt]=f[pre]+1;
}
}
if(D*A<=B) sub1();
else sub2();
return 0;
}