专栏文章
题解:P12088 [RMI 2019] 还原 / Restore Arrays
P12088题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mipo9iaa
- 此快照首次捕获于
- 2025/12/03 15:15 3 个月前
- 此快照最后确认于
- 2025/12/03 15:15 3 个月前
前言
如果你写的是正常的 SPFA,那么恭喜你,会喜提 分的高分以及获得 TLE 三到四个点。
Solution
前置知识:差分约束
首先本题限制了区间 到 的第 小的元素为 ()。
我们可以将其进行分类讨论:
- 若 ,则有区间 到 中 的个数 ,即 的个数 。( 为区间长度。)
- 若 ,则有区间 到 中 的个数 ,即 的个数 ,可以推出 的个数 。
我们可以设一个 表示前 个数的前缀和。则 可以表示为区间 到 中 的个数。
我们就可以将上述的讨论进行转化:
因为 表示的是前缀和且数只有 或 ,所以同时还要满足以下条件:
之后就可以愉快的使用差分约束 AC 本题了。
注:这里的差分约束要用 Bellman-Ford,若使用 SPFA 你将会 TLE 的很惨。
Code
CPP#include <bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
using namespace std;
const int N=5e3+5;
ll n,m,s[N],cnt[N];
vector<array<int,3>> to;
int main(){
IOS;cin>>n>>m;
memset(s,0x7f,sizeof s);
for(int i=1;i<=n;i++){
to.pb({i-1,i,1});
to.pb({i,i-1,0});
}
for(int i=1,l,r,k,v;i<=m;i++){
cin>>l>>r>>k>>v;
l++;r++;//在这里将其转化为下标从一开始
if(v) to.pb({r,l-1,-(r-l+1-k+1)});
else to.pb({l-1,r,r-l+1-k});
}
s[0]=0;
bool flag=0;
for(int i=1;i<=n;i++){//Bellman-Ford
flag=0;
for(auto e:to){
int u=e[0],v=e[1],w=e[2];
if(s[v]>s[u]+w){
flag=1;
s[v]=s[u]+w;
}
}
if(!flag) break;
}
if(flag){
cout<<"-1\n";
return 0;
}
for(int i=1;i<=n;i++){
cout<<s[i]-s[i-1]<<" ";
}
return 0;
}
后记
关于 SPFA,它又活了。
你可以使用一些玄学优化来通过本题,就像这样:
CPP memset(s,0x7f,sizeof s);
queue<int> q;
q.push(0);
vis[0]=1;s[0]=0;
int tt=0;
while(!q.empty()){//SPFA
int u=q.front();q.pop();
vis[u]=0;
for(PLL tp:to[u]){
int v=tp.first,w=tp.second;
tt++;
if(tt>2e7){//玄学优化
cout<<-1<<"\n";
return 0;
}
if(s[u]+w<s[v]){
s[v]=s[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n){
cout<<"-1\n";
return 0;
}
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
for(int i=1;i<=n;i++) cout<<s[i]-s[i-1]<<" ";
不过不建议在考试时使用。
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...