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\) 的结果
把转移换个方式写出来,我们需要维护的东西就显然了:
- 前缀加数
- 在最后插入数
- 查询最大值
用线段树维护可以做到 \(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;
}

浙公网安备 33010602011771号