YbtOJ#463-序列划分【二分答案,线段树,dp】
正題
題目鏈接:https://www.ybtoj.com.cn/problem/463
題目大意
給出長(zhǎng)度為nnn的序列A,BA,BA,B。要求劃分成若干段滿足
要求最小化每一段BiB_iBi?和的最大值。
n∈[1,105],Ai,Bi∈[1,109],m∈[1,1012]n\in[1,10^5],A_i,B_i\in[1,10^9],m\in[1,10^{12}]n∈[1,105],Ai?,Bi?∈[1,109],m∈[1,1012]
解題思路
最大值最小化很顯然直接二分,然后變?yōu)榍竺恳欢?span id="ozvdkddzhkzd" class="katex--inline">AiA_iAi?最大值的和的最小值。
第一個(gè)條件相當(dāng)于限制了什么位置能夠作為劃分段的末尾,求一個(gè)前綴min{bi}min\{b_i\}min{bi?}和一個(gè)后綴max{ai}max\{a_i\}max{ai?}能夠快速求出這些位置。
然后考慮dpdpdp,轉(zhuǎn)移方程就是
fi=min{fj+max{ak}(k∈(j,i])}f_i=min\{f_j+max\{a_k\}(\ k\in(j,i]\ )\}fi?=min{fj?+max{ak?}(?k∈(j,i]?)}
二分的條件限制了jjj的范圍,加個(gè)指針就好了
這個(gè)東西好像很難搞,但是注意到vj=max{ak}v_j=max\{a_k\}vj?=max{ak?}這一部分是遞減的,并且每次會(huì)讓所有viv_ivi?的一起和一個(gè)一起取maxmaxmax。
因?yàn)槭沁f減的,所以每次加入一個(gè)新的就相當(dāng)于修改一段后綴的viv_ivi?,然后求一個(gè)區(qū)間的最大fi+vif_i+v_ifi?+vi?了。
可以線段樹(shù)維護(hù),每個(gè)節(jié)點(diǎn)維護(hù)該區(qū)間最大的fi+vif_i+v_ifi?+vi?和最大的fif_ifi?。區(qū)間推平viv_ivi?的時(shí)候就可以拿最大的fif_ifi?來(lái)更新fi+vif_i+v_ifi?+vi?
時(shí)間復(fù)雜度O(nlog?nlog?∑bi)O(n\log n\log\sum b_i)O(nlognlog∑bi?)
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=1e5+10,inf=1e9+7; ll n,m,a[N],b[N],pre[N],suf[N],last[N]; ll lg[N],st[N][17],v[N<<2],w[N<<2],lazy[N<<2]; void Downdata(ll x){if(!lazy[x])return;lazy[x*2]=lazy[x*2+1]=lazy[x];w[x*2]=v[x*2]+lazy[x];w[x*2+1]=v[x*2+1]+lazy[x];lazy[x]=0;return; } void Changew(ll x,ll L,ll R,ll l,ll r,ll val){if(l>r)return;if(L==l&&R==r){w[x]=v[x]+val;lazy[x]=val;return;}ll mid=(L+R)>>1;Downdata(x);if(r<=mid)Changew(x*2,L,mid,l,r,val);else if(l>mid)Changew(x*2+1,mid+1,R,l,r,val);else Changew(x*2,L,mid,l,mid,val),Changew(x*2+1,mid+1,R,mid+1,r,val);w[x]=min(w[x*2],w[x*2+1]); } void Changev(ll x,ll l,ll r,ll pos,ll val){if(l==r){v[x]=val;w[x]=v[x]+lazy[x];return;}ll mid=(l+r)>>1;Downdata(x);if(pos<=mid)Changev(x*2,l,mid,pos,val);else Changev(x*2+1,mid+1,r,pos,val);w[x]=min(w[x*2],w[x*2+1]);v[x]=min(v[x*2],v[x*2+1]);return; } ll Ask(ll x,ll L,ll R,ll l,ll r){if(L==l&&R==r)return w[x];ll mid=(L+R)>>1;Downdata(x);if(r<=mid)return Ask(x*2,L,mid,l,r);if(l>mid)return Ask(x*2+1,mid+1,R,l,r);return min(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r)); } ll RMQ(ll l,ll r){ll z=lg[r-l+1];return max(st[l][z],st[r-(1<<z)+1][z]); } bool check(ll x){memset(v,0x3f,sizeof(v));memset(w,0x3f,sizeof(w));memset(lazy,0,sizeof(lazy));ll sum=0,l=0,tmp=v[0];Changev(1,0,n,0,0);for(ll i=1;i<=n;i++){sum+=b[i];while(sum>x)l++,sum-=b[l];Changew(1,0,n,last[i],i-1,a[i]);if(pre[i]<=suf[i+1])continue;tmp=Ask(1,0,n,l,i);Changev(1,0,n,i,tmp);}return (tmp<=m); } signed main() {freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);scanf("%lld%lld",&n,&m);ll l=1,r=0;pre[0]=inf;for(ll i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]),r+=b[i],l=max(l,b[i]),st[i][0]=a[i];for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1;for(ll j=1;(1<<j)<=n;j++)for(ll i=1;i+(1<<j)-1<=n;i++)st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);for(ll i=1;i<=n;i++){ll l=1,r=i-1;while(l<=r){ll mid=(l+r)>>1;if(RMQ(mid,i)>a[i])l=mid+1;else r=mid-1;}last[i]=r;}for(ll i=1;i<=n;i++)pre[i]=min(pre[i-1],b[i]);for(ll i=n;i>=1;i--)suf[i]=max(suf[i+1],a[i]);while(l<=r){ll mid=(l+r)>>1;if(check(mid))r=mid-1;else l=mid+1;}check(l+1);printf("%lld\n",l); } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的YbtOJ#463-序列划分【二分答案,线段树,dp】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 夜神安卓模拟器访问登录 Google P
- 下一篇: YbtOJ#573-后缀表达【二分图匹配