qoj7905 Ticket to Ride 题解

题目链接
牛题!有很多技巧

把区间 \([x-1,x]\) 当成点 \(x\),先考虑朴素的 \(dp\)
\(f_{i,j}\) 表示前 \(i\) 个点,染了 \(j\) 个,且第 \(i\) 个点染色的最大价值,\(g_{i,j}\) 同理表示第 \(i\) 个点不染色的最大价值
转移是显然的:\(g_{i,j}=max\{g_{i-1,j},f_{i-1,j}\}\)\(f_{i,j}=\max\limits_{i'=0}^{i-1}\{g_{i',j-(i-i')}+w_{i'+1,i}\}\),其中 \(w_{l,r}\) 表示区间 \([l,r]\) 全部染色的价值

这里有一个 \(trick\) 是发现到 \(i-j=i'-(j-(i-i'))\),这说明了只有 \(i-j\) 相同的 \(f\)\(g\) 之间才能转移
这启发我们用数据结构维护 \(i-j\) 相同的所有 \(i\) 的结果
把转移换个方式写出来,我们需要维护的东西就显然了:

  1. 前缀加数
  2. 在最后插入数
  3. 查询最大值

用线段树维护可以做到 \(O(n^2\log n)\)

$O(n^2\log n)$ 的代码
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=1010;
int n,m;
LL f[N][N],g[N][N];
vector<pii> w[N];
struct SGT{
    LL mx[N<<2],tag[N<<2];int pos[N<<2];
    void down(int x,LL v){ mx[x]+=v,tag[x]+=v;}
    void pushdown(int x){ if(tag[x]) down(x<<1,tag[x]),down(x<<1^1,tag[x]),tag[x]=0;}
    void pushup(int x){
        mx[x]=max(mx[x<<1],mx[x<<1^1]);
        if(mx[x]==mx[x<<1]) pos[x]=pos[x<<1];else pos[x]=pos[x<<1^1];
    }
    void add(int l,int r,int x,int L,int R,LL v){
        if(L<=l&&r<=R){ down(x,v);return;}
        pushdown(x);
        int mid=(l+r)>>1;
        if(mid>=L) add(l,mid,x<<1,L,R,v);
        if(mid<R) add(mid+1,r,x<<1^1,L,R,v);
        pushup(x);
    }
    void build(int l,int r,int x){
        mx[x]=tag[x]=0;
        if(l==r){ pos[x]=l;return;}
        int mid=(l+r)>>1;
        build(l,mid,x<<1),build(mid+1,r,x<<1^1);
        pushup(x);
    }
    void clr(){ F(i,1,n<<2) mx[i]=tag[i]=0;}
}sgt[N];
void work(){
    read(n),read(m);
    F(i,1,n) w[i].clear();
    F(i,1,m){
        int l,r,v;read(l),read(r),read(v),l++;
        w[r].pb({l,v});
    }
    F(i,1,n) F(j,0,i) f[i][j]=g[i][j]=0;
    F(i,0,n) sgt[i].build(0,n,1);
    F(i,1,n){
        F(j,0,i) g[i][j]=max(f[i-1][j],g[i-1][j]);
        for(auto [l,v]:w[i]) F(j,0,l-1) sgt[j].add(0,n,1,0,l-1,v);
        F(j,1,i) f[i][j]=sgt[i-j].mx[1];
        F(j,0,i) sgt[i-j].add(0,n,1,i,i,g[i][j]);
        // F(j,1,i) F(k,0,i-1) if(j>=i-k) chkmax(f[i][j],g[k][j-(i-k)]+w[k+1][i]);
    }
    F(i,1,n) printf("%lld ",max(f[n][i],g[n][i]));puts("");
}
int main(){
    freopen("in.in","r",stdin);
    int T;read(T);
    while(T--) work();
    return 0;
}

通过上面的操作,我们发现最大数的位置要么瞬移到最后插入数的位置,要么往前移
考虑用并查集维护每个位置前缀最大数的位置,和后面第一个大于当前数的位置
说实话,这一部分的维护挺难的
时间复杂度 \(O(n^2)\),不算并查集的复杂度
具体实现可以看代码

$O(n^2)$ 的代码
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=10010;
int n,m;
LL f[N],g[N],ans[N];
vector<pii> w[N];
struct DSU{
    int mxp,fa[N],nxt[N];
    LL mx,lef[N];
    //fa[x]:x的前缀中最大的数的位置(跳fa)
    int gfa(int x){ return x==fa[x]?x:fa[x]=gfa(fa[x]);}
    void init(){
        mx=mxp=0;
        F(i,0,n) fa[i]=nxt[i]=lef[i]=0;
    }
    void ins(int p,LL v){
        if(v>mx) lef[mxp]=v-mx,nxt[mxp]=p,mxp=p,mx=v,fa[p]=p;
        else fa[p]=mxp;
    }
    void add(int p,LL v){
        p=gfa(p);
        while(p!=mxp){
            if(lef[p]>=v){ lef[p]-=v;break;}
            v-=lef[p];
            int q=nxt[p];fa[q]=p;
            if(q==mxp) mxp=p;
            else lef[p]=lef[q],nxt[p]=nxt[q];
        }
        if(p==mxp) mx+=v;
    }
}dsu;
LL tg[N],tf[N];
void work(){
    read(n),read(m);
    F(i,1,n) w[i].clear();
    F(i,1,m){
        int l,r,v;read(l),read(r),read(v),l++;
        w[r].pb({l,v});
    }
    F(j,1,n) tg[j]=tf[j]=f[j]=g[j]=0;
    F(i,0,n-1){
        dsu.init();
        F(j,1,n){
            for(auto [l,v]:w[j]) if(i<l) dsu.add(l-1,v);
            g[j]=max(tf[j-1],tg[j-1]),f[j]=dsu.mx;
            dsu.ins(j,g[j]);
        }
        F(j,1,n) tg[j]=g[j],tf[j]=f[j];
        ans[n-i]=max(f[n],g[n]);
    }
    F(i,1,n) printf("%lld ",ans[i]);puts("");
}
int main(){
    int T;read(T);
    while(T--) work();
    return 0;
}

posted @ 2025-02-08 22:23  Farmer_D  阅读(56)  评论(0)    收藏  举报