luogu P11432
P11432 [COCI 2024/2025 #2] 流明 / Blistavost
思路:
首先要挖掘性质,模拟一下关灯的过程可以发现关一个区间中的灯时,首尾的灯一定是要被关掉的,也就是说当首尾的灯被关掉之后,区间中的所有灯都可以被关掉。那么就可以把区间中的无数盏灯关闭的问题转化成关闭区间首尾的灯的问题。
那么可以将这些首尾灯单独拎出来,按照坐标大小排序。
然后可以想到,如果说 \([l,r]\) 中的 \(l,r\) 都没有被关,(\(l,r\) 是灯在坐标数组中的下标),是需要把\([l,r]\)中所有的点都再跑一遍,这样才能关掉 \(l,r\) 两盏灯的,那么跑这一趟其实完全可以等价于只关 \(l,r\),保持剩下的所有灯全部亮着,也就是 \((l,r)\) 为一段亮着的灯的连续区间,由此可以想出状态的定义:
设 \(f_{i,j,0}\) 表示 \((i,j]\) 中全为亮灯且剩余灯全部被关闭的最小时间,\(f_{i,j,1}\) 表示 \([i,j)\) 中全为亮灯且剩余灯全部被关闭的最小时间。
则有:
\[\begin{cases}
f_{i,j,0}=min\{max\{f_{i-1,j,0}+dis(i-1,i),t_i\},max\{f_{i,j+1,1}+dis(j+1,i),t_i\}\}\\
f_{i,j,1}=min\{max\{f_{i-1,j,0}+dis(i-1,j),t_j\},max\{f_{i,j+1,1}+dis(j,j+1),t_j\}\}
\end{cases}
\]
然后空间就爆炸了。
若固定 \(i\) 时,\(j\) 和 \(len\) 成正比,那么把 \(j\) 全部换成 \(len\),然后可以发现 \(f_{i,len,{0/1}}\) 仅和 \(f_{i,len+1,0/1}\) 有关,再滚动一下就可以了。
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e3+100;
struct light{
int pos,t,id;
friend bool operator<(light a,light b){
return a.pos<b.pos;
}
}a[N];
int ans=4e18,tot,f[N][N][2],n;
inline int dis(int x,int y){
return abs(a[x].pos-a[y].pos);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int l,r,t;
cin>>l>>r>>t;
a[++tot]=light{l,t,tot};
a[++tot]=light{r,t,tot};
}
memset(f,0x6f,sizeof(f));
sort(a+1,a+1+tot);
f[1][tot][1]=max(a[tot].t,a[tot].pos);//[1,n)
f[1][tot][0]=max(a[1].t,a[1].pos);
for(int len=tot-1;len>=1;len--){
for(int i=1;i+len-1<=tot;i++){
int j=i+len-1;
if(i-1){
f[i][len][0]=min(f[i][len][0],max(f[i-1][len+1][0]+dis(i-1,i),a[i].t));
f[i][len][1]=min(f[i][len][1],max(f[i-1][len+1][0]+dis(i-1,j),a[j].t));
}
if(j+1<=tot){
f[i][len][0]=min(f[i][len][0],max(f[i][len+1][1]+dis(i,j+1),a[i].t));
f[i][len][1]=min(f[i][len][1],max(f[i][len+1][1]+dis(j,j+1),a[j].t));
}
if(len==1){
ans=min(ans,f[i][len][0]);
ans=min(ans,f[i][len][1]);
}
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号