门童
题目概述
题解
妹妹最爱的树套树优化dp。
首先应该很容易 come up with 一种
d
p
dp
dp的思路。
由于牛牛整个过程中只有两个动作,待在门口,跑回去休息又跑回来。
我们可以定义
d
p
i
,
0
/
1
dp_{i,0/1}
dpi,0/1表示牛牛现在是待在门口还是再休息,但这样的话处理接待客人是很麻烦的,可能还要状压,我们不如就只看他待在门口接待客人的时候。
定义
d
p
i
dp_{i}
dpi表示他在时间
i
i
i时待在门口的总贡献,有两种转移,一种就是上一个时刻也待在门口,一种是从之前某个时刻跑回去休息,现在跑回来。
第一种的话客人一来就可以接待,直接加上客人的总价值就行了。
而第二种相当于在这段区间的客人来的时候,你是不会接待他们的,只有当你跑会来时才会接待他们,这就要算中间这段时间的贡献了。
我们同时发现,这样的转移第二种转移同时也是有区间的限制的,你不可能完全错过某个客人。
但我们发现如果像我们上面说的这样
d
p
dp
dp的话,我们发现真正有效的时间节点只有某个人到来的时间和某个人离开的时间。
因为我们的
d
p
dp
dp转移产生的贡献事实上都是斜率固定的直线的形式,所以加起来我们的最优决策点肯定时在某个时间节点上的。
我们可以只拿时间节点来作为
d
p
dp
dp状态,定义第
i
i
i个时间节点为
b
i
b_{i}
bi。
显然可以得到
d
p
dp
dp转移式,
d
p
i
=
max
(
d
p
i
−
1
−
(
b
i
−
b
i
−
1
)
x
0
,
0
+
∑
t
k
∈
(
b
i
−
1
,
b
i
]
f
k
p
k
,
max
j
=
L
i
R
i
(
d
p
j
−
L
(
x
1
,
0
+
x
0
,
1
+
2
x
1
,
1
)
+
(
b
i
−
b
j
−
2
L
)
x
1
,
1
+
∑
t
k
∈
(
b
i
−
1
,
b
i
]
f
k
(
t
k
+
p
k
−
b
i
)
)
)
dp_{i}=\max\left(dp_{i-1}-(b_{i}-b_{i-1})x_{0,0}+\sum_{t_{k}\in(b_{i-1},b_{i}]}f_{k}p_{k},\max_{j=L_{i}}^{R_{i}}\left(dp_{j}-L(x_{1,0}+x_{0,1}+2x_{1,1})+(b_{i}-b_{j}-2L)x_{1,1}+\sum_{t_k\in(b_{i-1},b_{i}]}f_k(t_k+p_k-b_i)\right)\right)
dpi=max⎝⎛dpi−1−(bi−bi−1)x0,0+tk∈(bi−1,bi]∑fkpk,j=LimaxRi⎝⎛dpj−L(x1,0+x0,1+2x1,1)+(bi−bj−2L)x1,1+tk∈(bi−1,bi]∑fk(tk+pk−bi)⎠⎞⎠⎞
对于
L
i
L_{i}
Li和
R
i
R_{i}
Ri,这两者显然是不降的,我们应该可以很容易地
O
(
n
)
O\left(n\right)
O(n)处理出来。
而对于上面的
d
p
dp
dp式子,我们是可以再化一下的,
d
p
i
=
max
(
d
p
i
−
1
−
(
b
i
−
b
i
−
1
)
x
0
,
0
+
∑
t
k
∈
(
b
i
−
1
,
b
i
]
f
k
p
k
,
b
i
x
1
,
1
−
L
(
x
1
,
0
+
x
0
,
1
+
2
x
1
,
1
)
+
max
j
=
L
i
R
i
(
d
p
j
−
b
j
x
1
,
1
+
∑
t
k
∈
(
b
i
−
1
,
b
i
]
f
k
(
t
k
+
p
k
−
b
i
)
)
)
dp_{i}=\max\left(dp_{i-1}-(b_{i}-b_{i-1})x_{0,0}+\sum_{t_{k}\in(b_{i-1},b_{i}]}f_{k}p_{k},b_{i}x_{1,1}-L(x_{1,0}+x_{0,1}+2x_{1,1})+\max_{j=L_{i}}^{R_{i}}\left(dp_{j}-b_{j}x_{1,1}+\sum_{t_{k}\in(b_{i-1},b_{i}]}f_{k}(t_{k}+p_{k}-b_{i})\right)\right)
dpi=max⎝⎛dpi−1−(bi−bi−1)x0,0+tk∈(bi−1,bi]∑fkpk,bix1,1−L(x1,0+x0,1+2x1,1)+j=LimaxRi⎝⎛dpj−bjx1,1+tk∈(bi−1,bi]∑fk(tk+pk−bi)⎠⎞⎠⎞
这样的话其实是比较显而易见的斜率优化了,我们再看一看,化简一下,
定义
s
u
m
i
=
∑
t
k
⩽
b
i
f
k
(
t
k
+
p
k
)
sum_{i}=\sum_{t_{k}\leqslant b_{i}}f_{k}(t_{k}+p_{k})
sumi=∑tk⩽bifk(tk+pk),
s
u
m
p
i
=
∑
t
k
⩽
b
i
f
k
p
k
sump_{i}=\sum_{t_{k}\leqslant b_{i}}f_{k}p_{k}
sumpi=∑tk⩽bifkpk,
s
u
m
f
i
=
∑
t
k
⩽
b
i
f
k
sumf_{i}=\sum_{t_{k}\leqslant b_{i}}f_{k}
sumfi=∑tk⩽bifk
那么有
d
p
i
=
max
(
d
p
i
−
1
−
(
b
i
−
b
i
−
1
)
x
0
,
0
+
s
u
m
p
i
−
s
u
m
p
i
−
1
,
b
i
x
1
,
1
−
L
(
x
1
,
0
+
x
0
,
1
+
2
x
1
,
1
)
+
max
j
=
L
i
R
i
(
d
p
j
+
s
u
m
i
−
s
u
m
j
−
b
j
x
1
,
1
+
(
s
u
m
f
i
−
s
u
m
f
j
)
b
i
)
)
dp_{i}=\max\left(dp_{i-1}-(b_{i}-b_{i-1})x_{0,0}+sump_{i}-sump_{i-1},b_{i}x_{1,1}-L(x_{1,0}+x_{0,1}+2x_{1,1})+\max_{j=L_{i}}^{R_{i}}\left(dp_{j}+sum_{i}-sum_{j}-b_{j}x_{1,1}+(sumf_{i}-sumf_{j})b_{i}\right)\right)
dpi=max(dpi−1−(bi−bi−1)x0,0+sumpi−sumpi−1,bix1,1−L(x1,0+x0,1+2x1,1)+j=LimaxRi(dpj+sumi−sumj−bjx1,1+(sumfi−sumfj)bi))
前面定义
c
o
s
t
i
=
(
b
i
−
b
i
−
1
)
x
0
,
0
+
s
u
m
p
i
−
s
u
m
p
i
−
1
cost_{i}=(b_{i}-b_{i-1})x_{0,0}+sump_{i}-sump_{i-1}
costi=(bi−bi−1)x0,0+sumpi−sumpi−1,再将后面根据与
i
i
i有关还是与
j
j
j有关划分一下,与
j
j
j无关的提出来。
定义
A
i
=
b
i
x
1
,
1
−
L
(
x
1
,
0
+
x
0
,
1
+
2
x
1
,
1
)
+
s
u
m
i
+
s
u
m
f
i
b
i
A_{i}=b_{i}x_{1,1}-L(x_{1,0}+x_{0,1}+2x_{1,1})+sum_{i}+sumf_{i}b_{i}
Ai=bix1,1−L(x1,0+x0,1+2x1,1)+sumi+sumfibi,
B
j
=
d
p
j
−
s
u
m
j
−
b
j
x
1
,
1
B_{j}=dp_{j}-sum_{j}-b_{j}x_{1,1}
Bj=dpj−sumj−bjx1,1,
C
j
=
s
u
m
f
j
C_{j}=sumf_{j}
Cj=sumfj,
D
i
=
b
i
D_{i}=b_{i}
Di=bi,
那么原式就成了:
d
p
i
=
max
(
d
p
i
−
1
+
c
o
s
t
i
,
A
i
+
max
j
=
L
i
R
i
(
B
j
+
C
j
D
i
)
)
dp_{i}=\max\left(dp_{i-1}+cost_{i},A_{i}+\max_{j=L_{i}}^{R_{i}}(B_{j}+C_{j}D_{i})\right)
dpi=max(dpi−1+costi,Ai+j=LimaxRi(Bj+CjDi))
十分明显的斜率优化式子了,对于每个
d
p
i
dp_{i}
dpi,都有固定的
x
=
D
i
x=D_{i}
x=Di与
A
i
A_{i}
Ai,要在前面
[
L
i
,
R
i
]
[L_{i},R_{i}]
[Li,Ri]中找到直线
y
=
B
j
+
C
j
x
y=B_{j}+C_{j}x
y=Bj+Cjx找到最大的来转移。
既然是直线的转移,那便很容易想到通过李超线段树来维护了。
不过即使
L
i
,
R
i
L_{i},R_{i}
Li,Ri是递增的,李超线段树也不好维护呀,优先队列的做法我又打不来。
但是我们可以树套树呀,外层线段树维护区间,内层李超线段树维护我们的线段,这样就可以了。
树套树优化
d
p
dp
dp毕竟是传统艺能嘛。
其实也可以用分块来维护,这样空间可能会小一点,但时间复杂度会大一点。
但没关系,小常数都跑得很快。
就这样跑一道
d
p
dp
dp就可以了。
时间复杂度
O
(
n
log
2
n
)
O\left(n\log^2n\right)
O(nlog2n)(树套树)或者
O
(
n
n
log
n
)
O\left(n\sqrt{n\log\,n}\right)
O(nnlogn)(分块)。
优先队列好像可以做到
O
(
n
log
n
)
O\left(n\log\,n\right)
O(nlogn)。
至于标程的
O
(
n
log
2
n
)
O\left(n\log^2n\right)
O(nlog2n)李超树怎么弄的我没看懂。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
struct ming{int f,t,p;}s[MAXN];
struct line{
LL k,b;line(){k=0;b=-1e15;}
line(LL K,LL B){k=K;b=B;}
LL ask(const int x){return 1ll*x*k+b;}
};
int n,L,x00,x01,x10,x11,b[MAXN],tott,dL[MAXN],dR[MAXN],d[MAXN];
LL dp[MAXN],sumf[MAXN],summ[MAXN],ans,sump[MAXN];
struct tann{line s;int lson,rson;};
class SegmentTree{
private:
tann tr[MAXN*100];int tot,root[MAXN<<2];
void insert(int &rt,int l,int r,line aw){
if(l>r)return ;if(!rt)rt=++tot;int mid=l+r>>1;
if(tr[rt].s.ask(b[mid])<aw.ask(b[mid]))swap(tr[rt].s,aw);
if(l<mid&&tr[rt].s.ask(b[l])<aw.ask(b[l]))insert(tr[rt].lson,l,mid-1,aw);
if(r>mid&&tr[rt].s.ask(b[r])<aw.ask(b[r]))insert(tr[rt].rson,mid+1,r,aw);
}
LL query(int rt,int l,int r,int ai){
if(l>ai||r<ai||!rt)return -INF;
int mid=l+r>>1;LL res=tr[rt].s.ask(b[ai]);
if(ai<mid)res=max(res,query(tr[rt].lson,l,mid-1,ai));
if(ai>mid)res=max(res,query(tr[rt].rson,mid+1,r,ai));
return res;
}
public:
#define lson (rt<<1)
#define rson (rt<<1|1)
void Insert(int rt,int l,int r,int ai,line aw){
if(l>ai||r<ai)return ;int mid=l+r>>1;
insert(root[rt],0,tott,aw);if(l==r)return ;
if(ai<=mid)Insert(lson,l,mid,ai,aw);
else Insert(rson,mid+1,r,ai,aw);
}
LL Query(int rt,int l,int r,int al,int ar,int ai){
if(l>r||l>ar||r<al||al>ar)return -INF;
if(al<=l&&r<=ar)return query(root[rt],0,tott,ai);
int mid=l+r>>1;LL res=-INF;
if(al<=mid)res=max(res,Query(lson,l,mid,al,ar,ai));
if(ar>mid)res=max(res,Query(rson,mid+1,r,al,ar,ai));
return res;
}
#undef lson
#undef rson
}T;
signed main(){
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
read(n);read(L);int maxx=0;
read(x00);read(x01);read(x10);read(x11);
for(int i=1;i<=n;i++)
read(s[i].t),read(s[i].p),read(s[i].f),
b[++tott]=s[i].t,b[++tott]=s[i].t+s[i].p;
sort(b+1,b+tott+1);tott=unique(b+1,b+tott+1)-b-1;
for(int i=1;i<=n;i++){
int tmp=lower_bound(b+1,b+tott+1,s[i].t)-b;
summ[tmp]+=1ll*s[i].f*(s[i].t+s[i].p);
sumf[tmp]+=1ll*s[i].f;maxx=max(maxx,tmp);
sump[tmp]+=1ll*s[i].f*s[i].p;
int tp=upper_bound(b+1,b+tott+1,s[i].t+s[i].p)-b;d[tp]=max(d[tp],tmp);
}
for(int i=1;i<=tott;i++)summ[i]+=summ[i-1],sumf[i]+=sumf[i-1],sump[i]+=sump[i-1];
for(int i=1;i<=tott;i++)dR[i]=min(i-1,maxx-1);
for(int i=1;i<=tott;i++)dL[i]=max(dL[i-1],d[i]);
dp[0]=0;T.Insert(1,0,tott,0,line(0LL,0LL));
for(int i=1;i<=tott;i++){
dp[i]=dp[i-1]-1ll*(b[i]-b[i-1])*x00+sump[i]-sump[i-1];
LL tmpa=1ll*b[i]*x11-1ll*L*(x01+x10+x11+x11)+summ[i]-1ll*b[i]*sumf[i];
LL tmpb=T.Query(1,0,tott,dL[i],dR[i],i);dp[i]=max(dp[i],tmpb+tmpa);
T.Insert(1,0,tott,i,line(sumf[i],dp[i]-1ll*b[i]*x11-summ[i]));
if(i>=maxx)ans=max(ans,dp[i]);
}
printf("%lld\n",ans);
return 0;
}