P10430 [JOIST 2024] 鱼 3 / Fish 3 题解(做法二)
前面写过一个另外一个方法的,我自己想的。那个方法拉完了,实现非常复杂。
P10430 [JOIST 2024] 鱼 3 / Fish 3
注意,下面把问题转化为智商为\(c_i\),让智商降低为\(0\)。也就是说,所有操作的加减反转
我们容易发现,对于一个询问。等价于我们要用最少的操作A的次数使得区间单调不降。
我们先考虑暴力做法。
我们先确定区间的右端点。
记当前枚举的区间为 \([l,r]\)
然后我们从右往做枚举。对于\(l \leq i,i+1 \leq r\) 诺 \(c_i>c_i+1\)。 则意味着\(c_i\)要减小,直到不大于\(c_{i+1}\)。
然后我们很容易发现一个性质,对于任意一个连续的区间\([l',r']\) 诺这个区间内相连两个数的差均小于\(d\)则\(c_{r'}\) 减少\(kd\),整个\([l',rl]\)区间内所有的数都至少得跟着减少相同的值。
这使得我们不用一个个枚举。一次可以之间枚举一个区间。
我们将一个相连两个数的差均小于\(d\)的区间称做绑定区间。
对于两个相邻的绑定区间 \(A,B\) \(B\)在\(A\)左侧显然,有时候\(B\)减少\(A\)也得跟着减少。
我们发现,这个现象出现在\(A,B\) 两个区间之间的差小于\(d\)的时候。在小于\(d\),\(A\)和\(B\)可以构成一个新的绑定区间。
那么我们在处理的时候,可以尝试合并相连的绑定区间。这样以后的复杂度会更优。
显然,合并区间的总复杂度为\(O(n)\) 。
然后我们如何实现区间整体减法,然后判断两个区间相连处的差呢?很显然,线段树解决。
但是每次询问都要经理这个过程还是太慢了。
我们考虑离线处理。
我们从对于每个区间内的枚举,变成整体\([1,n]\)的枚举
然后我们每次枚举完\(i\),就计算那些右端点为\(i\)的区间的答案。
我们先考虑什么情况下不合法。我们维护的是最少的操作\(A\)的次数,只有在某个元素经不起这么多次操作\(A\)的时候,这个区间无法完成任务。
具体的,我们每次区间减法的时候维护一下\(c\)的具体值,然后看询问区间内有没有具体值为负数的。如果有,这个区间就无法完任务。
诺可以完成任务,我们如何计算进行了多少次\(A\)操作呢?显然,我们前面每次\(A\)操作的时候进行了区间线段树维护。那区间进行\(A\)操作的次数也就顺便维护了。
当然,你不肯一个个减去\(d\),你肯定得计算方法一次减去多个\(d\)的,这个非常简单。
复杂度多少?
\(n\)次合并\(n\)次枚举\(q\)个询问
故 \(O((n+q)log_2n)\)
#include <bits/stdc++.h>
#define ls(x) ((x)<<1)
#define rs(x) ((x)<<1|1)
using namespace std;
typedef long long ll;
const ll inf=1e18;
const ll maxn=3e5+5;
inline ll read() {
long long x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
struct Task{
ll l,r,id;
};
struct node{
ll sum,tag,minn; //cnt代表-d了多少次。tag代表懒标记。min代表在arr序列上修改后的值。
};
struct range{
ll l,r;
};
node seg[maxn<<2];// 线段树维护操作数
stack<range> sta;
ll arr[maxn],ans[maxn],up[maxn];
Task task[maxn];
ll n,d,q;
void push_down(ll p,ll l,ll r) {
if(!seg[p].tag) {
return;
}
ll mid=(l+r)>>1;
seg[ls(p)].tag+=seg[p].tag;
seg[ls(p)].sum+=seg[p].tag*(mid-l+1);
seg[ls(p)].minn-=seg[p].tag*d;
seg[rs(p)].tag+=seg[p].tag;
seg[rs(p)].sum+=seg[p].tag*(r-mid);
seg[rs(p)].minn-=seg[p].tag*d;
seg[p].tag=0;
}
void push_up(ll p) {
seg[p].sum=seg[ls(p)].sum+seg[rs(p)].sum;
seg[p].minn=min(seg[ls(p)].minn,seg[rs(p)].minn);
return;
}
void upd(ll p,ll l,ll r,ll x,ll y,ll z) {
if(x<=l&&r<=y) {
seg[p].tag+=z;
seg[p].sum+=(r-l+1)*z;
seg[p].minn-=z*d;
return;
}
push_down(p,l,r);
ll mid=(l+r)>>1;
if(x<=mid) {
upd(ls(p),l,mid,x,y,z);
}
if(y>mid) {
upd(rs(p),mid+1,r,x,y,z);
}
push_up(p);
return;
}
ll ask(ll p,ll l,ll r,ll x,ll y) {
if(x<=l&&r<=y) {
return seg[p].sum;
}
push_down(p,l,r);
ll mid=(l+r)>>1,ret=0;
if(x<=mid) {
ret+=ask(ls(p),l,mid,x,y);
}
if(y>mid) {
ret+=ask(rs(p),mid+1,r,x,y);
}
return ret;
}
ll query(ll p,ll l,ll r,ll x,ll y) {
if(x<=l&&r<=y) {
return seg[p].minn;
}
push_down(p,l,r);
ll mid=(l+r)>>1,ret=inf;
if(x<=mid) {
ret=query(ls(p),l,mid,x,y);
}
if(y>mid) {
ret=min(query(rs(p),mid+1,r,x,y),ret);
}
return ret;
}
ll get(ll x) {
return query(1,1,n,x,x);
}
void build(ll p,ll l,ll r) {
if(l==r) {
seg[p].minn=arr[l];
return;
}
//push_down(p,l,r);
ll mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
return;
}
void merge() {
if(sta.size()<2) {
return;
}
range tmp;
tmp.r=sta.top().r;
sta.pop();
tmp.l=sta.top().l;
sta.pop();
sta.push(tmp);
return;
}
void show() {
for(ll i=1;i<=n;++i) {
printf("%lld ",get(i));
}
puts("");
}
int main() {
// freopen("input.txt","r",stdin);
n=read();
d=read();
for(ll i=1;i<=n;++i) {
arr[i]=read();
}
q=read();
for(ll i=1;i<=q;++i) {
cin>>task[i].l>>task[i].r;
task[i].id=i;
}
sort(task+1,task+1+q,[](const Task&a,const Task&b){
return a.r<b.r;
});
build(1,1,n);
sta.push({1,1});
ll tot=1;
while(task[tot].r==1) {
ans[task[tot].id]=0;
++tot;
}
for(ll i=2;i<=n;++i) {
//show();
ll pre=get(i-1);
ll now=get(i);
if(now>pre) {
//构成新的连续段或者加入旧的连续段
if(now-pre>=d) {
//构成新的连续段‘
sta.push({i,i});
}else{
//插入旧的连续段
sta.push({i,i});
merge();
}
}else{
//我们尝试-d.
ll div=(get(i-1)-get(i))/d;
if((get(i-1)-get(i))%d) {
++div;
}
while(div) {
//分类,是否要合并s
if(sta.top().l==1) {
//肯定不需要合并了 没法合并
upd(1,1,n,1,sta.top().r,div);
div=0;
continue;
}
//检查是否需要合并
ll tmp=(get(sta.top().l)-get(sta.top().l-1))/d;
if(div>=tmp) {
//需要合并
upd(1,1,n,sta.top().l,sta.top().r,tmp);
merge();
div-=tmp;
}else{
//不需要合并
upd(1,1,n,sta.top().l,sta.top().r,div);
div=0;
}
}
sta.push({i,i});
merge();
}
//show();
//算答案
while(tot<=q && task[tot].r==i) {
Task now=task[tot];
++tot;
if(query(1,1,n,now.l,now.r)<0) {
ans[now.id]=-1;
continue;
}
ans[now.id]=ask(1,1,n,now.l,now.r);
}
}
for(ll i=1;i<=q;++i) {
printf("%lld\n",ans[i]);
}
return 0;
}

浙公网安备 33010602011771号