fogflea
fogflea
发布于 2025-09-06 / 11 阅读
0
0

[JOISC 2020] 治療計画

题目传送门

卧槽这题有点牛逼。

像我这种蒟蒻只看题面肯定没有什么头绪,所以只好看一眼数据范围,似乎只能依赖方案在 O(m\log) 内解决。

至于为什么会想到 dp ,应该只能靠感觉吧,这题一看就很有线性dp的味道。

所以由前面的想法可以大概先设个 f_i ,至于 i 的含义只能结合方案的性质和所求的东西来考虑。

先整理一下:一个方案由四个参数 T_i,L_i,R_i,C_i确定,而我们需要最小化 [1,n] 全部被覆盖的代价和。

那么 i 的含义只和 T_i,L_i,R_i 有关,而我们的最终状态中没有时间的限制,而 L_i,R_i 在地位上是对等的,所以我们不妨只从 R_i 考虑对 i 含义的设计。

接下来这一步应该只能靠经验了,因为最终状态是全部覆盖,所以可以看成是由前缀全部覆盖递推而来,落到状态设计上就是设 f_i 为覆盖前缀 [1,R_i] 的最小代价。

这样就可以自然的写出方程:

f_i=\min_{R_j-L_i+1\geq|T_j-T_i|}f_j+C_i

残念ながら...这是一个有后效性的方程,似乎并不能按照某种拓扑序转移,这时就要用上另一个套路。

给方程换一种写法:

f_i\leq f_j+C_i ,R_j-L_i+1\geq|T_j-T_i|

观察到方程很像一个三角不等式,启发我们往最短路去想,这时麻烦的只有边数是 O(m^2) 的,这时只能挖掘这张图的性质。

注意到 (这一步真的很自然) i 的所有入边权都是 C_i ,所以在 \texttt{Dijkstra} 中每个点只应被更新一次就可以得到最短路,剩下的都是无用更新,至多只有 O(m) 次松弛。

所以我们要做的事情就是每次取出优先队列的一个点,只松弛未被松弛过且满足条件的点,那么接下来就好做了,对于上面那个条件式子很容易用线段树维护。

#include<bits/stdc++.h>
using namespace std;
#define fio(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
#define tio() freopen("in.txt","r",stdin),freopen("out.txt","w",stdout)
using ll=long long;
const int N=1e5+10;
int rd(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }return x;
}
int n,m;
struct D{
    int t,l,r,c;
    int f0(){return -t+l-1;}
    int f1(){return t+l-1;}
}a[N];
ll f[N];
#define pii pair<ll,int>
priority_queue<pii,vector<pii>,greater<pii> >q;
struct st{
    struct node{
        int l,r,m;
        int v[2];
    }d[N<<2|1];
    void pushup(int p){
        int l=p<<1,r=l|1;
        d[p].v[0]=min(d[l].v[0],d[r].v[0]);
        d[p].v[1]=min(d[l].v[1],d[r].v[1]);
    }
    void build(int p,int l,int r){
        d[p]={l,r,l+r>>1,0};
        if(l==r){
            if(a[l].l==1){
                f[l]=a[l].c,q.push({f[l],l});
                d[p].v[0]=d[p].v[1]=2e9;
                return;
            }
            d[p].v[0]=a[l].f0();
            d[p].v[1]=a[l].f1();
            return;
        }
        build(p<<1,l,d[p].m);
        build(p<<1|1,d[p].m+1,r);
        pushup(p);
    }
    void modify(int p,int l,int r,int lim,int x,int op){
        if(l>r||lim<d[p].v[op])return;
        if(d[p].l==d[p].r){
            f[d[p].l]=f[x]+a[d[p].l].c;
            q.push({f[d[p].l],d[p].l});
            d[p].v[0]=d[p].v[1]=2e9;
            return ;
        }
        if(l<=d[p].m)modify(p<<1,l,r,lim,x,op);
        if(r>d[p].m)modify(p<<1|1,l,r,lim,x,op);
        pushup(p);
    }
}t;
int main(){
    m=rd(),n=rd();
    for(int i=1;i<=n;++i){
        a[i]={rd(),rd(),rd(),rd()};
        f[i]=1e18;
    }
    sort(a+1,a+1+n,[](D x,D y)->bool{return x.t<y.t;});
    t.build(1,1,n);
  
    while(!q.empty()){
        int x=q.top().second;q.pop();
        t.modify(1,1,x-1,a[x].r-a[x].t,x,0);
        t.modify(1,x+1,n,a[x].r+a[x].t,x,1);
    }
    ll ans=1e18;
    for(int i=1;i<=n;++i){
        if(a[i].r==m)
            ans=min(ans,f[i]);
    }
    printf("%lld\n",(ans==1e18)?-1:ans);
    return 0;
}

评论