https://codeforces.com/gym/101741/problem/D
首先有简单的dp,dp[i]=min{max{dp[j-1],t[i]}+2*max{a[j]--a[i]}} ,然后发现这个怎么都要n^2,好像没撒能维护的数据结构
然后要观察出一个性质,就是后面如果有比a[i]大的,当前这个一定跟后面那个更大的一起走,这个性质其实看下面化简了的dp式子也能看出来,这个性质有点像18桂林的Ahttps://blog.csdn.net/liufengwei1/article/details/109756288
然后我们合并一下,使得a[i]严格递减,dp就变成了dp[i]=min{max{dp[j-1],t[i]},2*a[j]}
那么对j二分一下,dp[j-1]<=t[i]的,dp[i]=t[i]+2*a[j],否则dp[i]=dp[j-1]+2*a[j],前面那个由于是a递减的,所以最小的a[j]就是边界上的,右边这个用线段树维护一下就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
const ll inf=1ll<<60;
int n,m,cnt,tot;ll ans;
int ctin[maxl],ca[maxl],b[maxl],sufmx[maxl];
ll tin[maxl],a[maxl],dp[maxl];
struct node
{
int l,r;
ll mi;
}tr[maxl*4];
inline void build(int k,int l,int r)
{
tr[k].l=l;tr[k].r=r;tr[k].mi=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
inline void prework()
{
for(int i=1;i<=n;i++)
scanf("%d%d",&ctin[i],&ca[i]);
sufmx[n]=ca[n];
for(int i=n-1;i>=1;i--)
sufmx[i]=max(sufmx[i+1],ca[i]);
cnt=0;sufmx[n+1]=0;
for(int i=1;i<=n;i++)
{
if(sufmx[i+1]>=ca[i])
continue;
b[++cnt]=i;
tin[cnt]=ctin[i];a[cnt]=ca[i];
}
n=cnt;
build(1,1,n);
}
inline void upd(int k,int l,ll x)
{
if(tr[k].l==tr[k].r)
{
tr[k].mi=x;
return;
}
int mid=(tr[k].l+tr[k].r)>>1;
if(l<=mid)
upd(k<<1,l,x);
else
upd(k<<1|1,l,x);
tr[k].mi=min(tr[k<<1].mi,tr[k<<1|1].mi);
}
inline ll getmi(int k,int l,int r)
{
if(tr[k].l==l && tr[k].r==r)
return tr[k].mi;
int mid=(tr[k].l+tr[k].r)>>1;
if(r<=mid)
return getmi(k<<1,l,r);
else if(l>mid)
return getmi(k<<1|1,l,r);
else
return min(getmi(k<<1,l,mid),getmi(k<<1|1,mid+1,r));
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
{
upd(1,i,dp[i-1]+2*a[i]);
if(dp[i-1]<=tin[i])
dp[i]=tin[i]+2*a[i];
else
{
int l=0;
for(int j=20;j>=0;j--)
if(l+(1<<j)<=i-1 && dp[l+(1<<j)]<=tin[i])
l+=(1<<j);
dp[i]=tin[i]+2*a[l+1];
dp[i]=min(dp[i],getmi(1,l+2,i));
}
}
}
inline void print()
{
printf("%lld\n",dp[n]);
}
int main()
{
while(~scanf("%d",&n))
{
prework();
mainwork();
print();
}
return 0;
}