题解:P16319 [ICPC 2023 Jinan R] 铁路环游
lailai0916 · · 题解
题意简述
有
对每个
解题思路
把没有建造的铁路称为断点。若恰好建造
从左到右处理铁路。设
若把第
下面考虑第
固定断点数
处理右端点为
问题归结为维护一个动态序列,操作只有三种:
- 在末尾插入一个数。
- 给指定前缀加上正数。
- 查询全局最大值。
只保留序列从左到右出现的严格前缀最大值。它们形成严格递增的记录点。普通位置只需归属于它左侧最近的记录点,因为它不可能单独决定全局最大值。
在相邻记录点之间保存最大值之差。设位置
若
用并查集维护每个原位置当前所属的记录点,并用链表连接相邻记录点。合并时把右侧记录点并入左侧记录点。之后从任何已合并位置出发,都能直接跳到仍然存在的记录点。
在固定的
当没有断点时,前
时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=10005;
struct Seq
{
int fa[N],to[N],id[N],cnt;
ll dif[N],mx;
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void clear()
{
cnt=mx=0;
fa[0]=to[0]=0;
}
void push(int p,ll v)
{
id[p]=++cnt;
fa[cnt]=cnt;
to[cnt]=0;
dif[cnt]=0;
if(v<=mx)
{
fa[cnt]=find(cnt-1);
return;
}
dif[cnt]=v-mx;
to[find(cnt-1)]=cnt;
mx=v;
}
void add(int p,ll v)
{
int x=find(id[p]);
while(to[x]&&dif[to[x]]<=v)
{
int y=to[x];
v-=dif[y];
fa[y]=x;
to[x]=to[y];
}
if(to[x])dif[to[x]]-=v;
else mx+=v;
}
}q;
int n,m;
ll f[2][N],g[2][N],ans[N],sum[N];
vector<pair<int,ll>> seg[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)seg[i].clear();
fill(sum,sum+n+1,0);
fill(ans,ans+n+1,0);
for(int i=1;i<=m;i++)
{
int l,r;
ll v;
cin>>l>>r>>v;
seg[r].push_back({l,v});
sum[r]+=v;
}
for(int i=1;i<=n;i++)sum[i]+=sum[i-1];
for(int i=1;i<=n;i++)
{
f[0][i]=sum[i];
g[0][i]=sum[i-1];
}
ans[n]=sum[n];
for(int i=1;i<n;i++)
{
int k=i,now=k&1,pre=now^1;
fill(f[now],f[now]+n+1,0);
fill(g[now],g[now]+n+1,0);
q.clear();
q.push(k,0);
for(int j=k+1;j<=n;j++)
{
for(auto [l,v]:seg[j])
{
if(l>=k)q.add(l,v);
}
g[now][j]=max(f[pre][j-1],g[pre][j-1]);
q.push(j,g[now][j]);
f[now][j]=q.mx;
}
ans[n-k]=max(f[now][n],g[now][n]);
}
for(int i=1;i<=n;i++)
{
if(i>1)cout<<' ';
cout<<ans[i];
}
cout<<'\n';
}
return 0;
}