题解:P17746 「RSOI-1C」序列 / sequence
出题人题解。
由于我们只能执行加法操作,不妨先求出目标序列与初始序列的差值数组
如果在任何位置出现了 -1。
接下来考虑如何用最少的区间覆盖这些
我们从左到右遍历
然后比较当前的覆盖量
因此,我们从优先队列中取出结束位置最小(最靠左)的区间,扣除多余的数量,直到
由于
可以使用 priority_queue 维护一个小根堆,里面存储「结束位置」和「区间数量」的 pair。这样每次取出结束位置最小的区间进行状态转移。
时间复杂度:遍历数组需要
空间复杂度
:::info[Code]
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i(a);i<=(b);++i)
#define req(i,a,b) for(int i(a);i>=(b);--i)
using namespace std;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
template<typename TP> inline TP read(TP &num)
{
TP x=0;
int f=0;
char ch=getchar();
while(ch<48||ch>57) f|=ch=='-',ch=getchar();
while(48<=ch&&ch<=57) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return num=f?-x:x;
}
template<typename ...Args> inline void read(Args &...args)
{
(read(args),...);
}
template<typename TP> inline void write(TP x)
{
(x<0)?(putchar('-'),x=-x):0;
(x>9)?(write(x/10),0):0;
putchar((x%10)^48);
}
template<typename TP> inline void writeln(TP x)
{
write<TP>(x);
puts("");
}
int n,k,a[1000005],b[1000005],d[1000005],cur,ans;
signed main()
{
read(n,k);
rep(i,1,n) read(a[i]);
rep(i,1,n) read(b[i]),d[i]=b[i]-a[i];
// 如果存在需要减去的值,无解输出 -1
if(any_of(d+1,d+n+1,[](int x){return x<0;})) return puts("-1"),0;
// 小根堆,存储 pair<区间结束位置,该结束位置的区间数量>
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> q;
rep(i,1,n)
{
// 弹出所有在当前位置 i 之前就已经失效的区间
while(!q.empty()&&q.top().first<i) cur-=q.top().second,q.pop();
// 如果当前覆盖的区间数量大于需要的数量,贪心地丢弃那些最先结束的区间
if(cur>d[i])
{
int p=cur-d[i];
while(p)
{
auto [dl,cnt]=q.top(); q.pop();
int m=min(p,cnt); cur-=m,p-=m,cnt-=m;
if(cnt>0) q.emplace(dl,cnt); // 如果没减完,重新加进队列
}
}
// 如果当前覆盖的区间数量不足,贪心地新建一直延伸到 i+k-1 的区间
if(cur<d[i])
{
q.emplace(i+k-1,d[i]-cur);
ans+=d[i]-cur;
cur=d[i];
}
}
writeln(ans);
return 0;
}
:::