题解:P11630 [WC2025] 士兵
更好的阅读体验
考虑一个很菜的 dp。假设
然后我们注意到如果我们想要让
如果我们不想要让
所以我们把上面那个转移方程的
第二个和第三个转移是简单的,一个是区间
第一个可以这样变换以下:
所以我们再维护一个
具体地,我们开两棵线段树,分别维护
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 1000006
using namespace std;
int T,n,m,bn,a[N],a_[N],a1_[N],b[N],c[N];
struct Segtree {
int tag[N<<2],tree[N<<2];
void addtag(int p,int x){tag[p]+=x,tree[p]+=x;}
void push_down(int p){addtag(p<<1,tag[p]),addtag(p<<1|1,tag[p]),tag[p]=0;}
void build(int p,int l,int r)
{
tag[p]=0,tree[p]=-1e15;if(l==r)return;
int mid=l+r>>1;build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}
void assign(int p,int l,int r,int k,int x)
{
if(l==r)return tree[p]=x,(void)0;
push_down(p);int mid=l+r>>1;
k<=mid?assign(p<<1,l,mid,k,x):assign(p<<1|1,mid+1,r,k,x);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
void update(int p,int l,int r,int L,int R,int x)
{
if(L<=l&&r<=R)return addtag(p,x);
push_down(p);int mid=l+r>>1;
if(L<=mid)update(p<<1,l,mid,L,R,x);
if(R>mid)update(p<<1|1,mid+1,r,L,R,x);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
int query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return tree[p];
push_down(p);int mid=l+r>>1,ret=-1e15;
if(L<=mid)ret=max(ret,query(p<<1,l,mid,L,R));
if(R>mid)ret=max(ret,query(p<<1|1,mid+1,r,L,R));
return ret;
}
}T1,T2;
void solve()
{
scanf("%lld%lld",&n,&m),c[bn=1]=0;
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i],&b[i]),c[++bn]=a[i],c[++bn]=a[i]-1;
sort(c+1,c+1+bn),bn=unique(c+1,c+1+bn)-c-1;
for(int i=1;i<=n;i++)
a_[i]=lower_bound(c+1,c+1+bn,a[i])-c;
for(int i=1;i<=n;i++)
a1_[i]=lower_bound(c+1,c+1+bn,a[i]-1)-c;
int _0=lower_bound(c+1,c+1+bn,0)-c;
T1.build(1,1,bn),T2.build(1,1,bn);
T1.assign(1,1,bn,_0,0),T2.assign(1,1,bn,_0,0);
for(int i=1;i<=n;i++)
{
int fai_1=max(T1.query(1,1,bn,a1_[i],a1_[i]),T1.query(1,1,bn,a_[i],bn));
int fai=max(T1.query(1,1,bn,a_[i],a_[i]),T2.query(1,1,bn,1,a1_[i])-m*a[i]);
T1.assign(1,1,bn,a_[i],fai),T2.assign(1,1,bn,a_[i],fai+m*c[a_[i]]);
T1.assign(1,1,bn,a1_[i],fai_1),T2.assign(1,1,bn,a1_[i],fai_1+m*c[a1_[i]]);
T1.update(1,1,bn,a_[i],bn,b[i]),T2.update(1,1,bn,a_[i],bn,b[i]);
}
printf("%lld\n",T1.tree[1]);
}
main()
{
scanf("%lld",&T);
while(T--)solve();
return 0;
}
题外话:
- 开题顺序 ABC:
100 + 58 + 5 ,Cu 线。 - 开题顺序 ACB:
100 + 58 + 100 ,Au 线。
场上我使用 ABC 的开题顺序,并且获得了压线铜牌。