1. 程式人生 > >【XSY2732】Decalcomania 可持久化線段樹 分治

【XSY2732】Decalcomania 可持久化線段樹 分治

lin %d == void ret 多少 for size include

題目描述

  有一個陶瓷瓶周圍有\(n\)個可以印花的位置。第\(i\)個與第\(i+1\)個位置之間的距離為\(d_i\),在第\(i\)個位置印圖案要\(t_i\)秒。

  機器剛開始在\(0\)號位置,可以以\(1\)單位長度每秒的速度移動。

  一個位置只能印一個圖案。

  現在有\(m\)秒,問你最多能印幾個圖案。

  保證時間不足以繞陶瓷瓶一圈。

  \(n\leq 100000\)

題解

  肯定是先往一邊移動在移動到另外一邊。

  不妨設先往右邊移動,那麽右邊的距離就要\(\times 2\)

  求出每邊印\(i\)個印花最少要多少秒。

  然後把兩邊合並即可。

  考慮怎麽求。

  顯然印\(i+1\)

個印花的最優方案所需要移動的最右端點肯定在印\(i\)個印花的右邊。

  證明:考慮反證法。

  設\(f(i,j)\)為印\(i\)個印花且最右端點為\(j\)的代價,\(a(i,j)\)為在前\(i\)個端點印印花所需要的第\(j\)短時間。

  設印\(i\)個印花的最優方案所需要移動的最右端點為\(j\)\(i+1\)個的右端點是\(k(k<j)\)

  那麽有\(f(i,j)<f(i,k),a(j,i+1)<a(k,i+1),f(i+1,j)>f(i+1,k)\)

  但是前兩項加起來是第三項,所以不合法。

  然後就可以分治了。

  設當前要求印\(l\)

個到\(r\)個的答案,答案區間為\([sl,sr]\)

  先求出印\(mid=\frac{l+r}{2}\)個的答案和右端點位置\(k\),可以通過枚舉右端點得到。

  \([l,mid-1]\)的右端點位置在\([sl,k]\)\([mid+1,r]\)的右端點位置在\([k,sr]\)

  然後分治下去即可。

  求前面\(i\)個位置最小的\(j\)個印印花的時間可以通過可持久化線段樹得到。

  時間復雜度:\(O(n\log^2n)\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility> using namespace std; typedef long long ll; typedef pair<int,int> pii; int q; char cc[10000010]; int tt; int h[100010]; ll rd() { ll s=0; int c; while((c=cc[tt++])<'0'||c>'9'); s=c-'0'; while((c=cc[tt++])>='0'&&c<='9') s=s*10+c-'0'; return s; } namespace seg { int ls[4000010]; int rs[4000010]; ll s[4000010]; int sz[4000010]; int cnt; int insert(int p1,int &x,int l,int r) { int p=++cnt; ls[p]=ls[p1]; rs[p]=rs[p1]; s[p]=s[p1]+h[x]; sz[p]=sz[p1]+1; if(l==r) return p; int mid=(l+r)>>1; if(x<=mid) ls[p]=insert(ls[p],x,l,mid); else rs[p]=insert(rs[p],x,mid+1,r); return p; } ll query(int p,int x,int l,int r) { if(l==r) return (ll)x*h[l]; int mid=(l+r)>>1; int lsz=sz[ls[p]]; if(x<=lsz) return query(ls[p],x,l,mid); return s[ls[p]]+query(rs[p],x-lsz,mid+1,r); } } int rt[100010]; int n; ll m; ll d[100010]; int t[100010]; ll a[100010]; ll f1[100010]; ll f2[100010]; ll g1[100010]; ll g2[100010]; int b[100010]; int c[100010]; int e[100010]; int f[100010]; ll &mm=m; void gao(int l,int r,int sl,int sr,ll *s) { if(l>r) return; int mid=(l+r)>>1; ll ans=0x3fffffffffffffffll; int i; int m=sr; for(i=sl;i<=sr;i++) if(i>=mid&&a[i-1]<=mm) { ll v=a[i-1]+seg::query(rt[i],mid,0,q); if(v<ans) { ans=v; m=i; } } s[mid]=ans; gao(l,mid-1,sl,m,s); gao(mid+1,r,m,sr,s); } void gao(ll *s) { memset(rt,0,sizeof rt); seg::cnt=0; int i; for(i=1;i<=n;i++) rt[i]=seg::insert(rt[i-1],c[i],0,q); gao(1,n,1,n,s); } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a2.out","w",stdout); #endif fread(cc+1,10000000,1,stdin); // scanf("%d%lld",&n,&m); n=rd(); m=rd(); int i; q=0; for(i=1;i<=n;i++) // scanf("%lld%lld",&d[i],&t[i]); { d[i]=rd(); t[i]=rd(); h[++q]=t[i]; } sort(h+1,h+q+1); q=unique(h+1,h+q+1)-h-1; for(i=1;i<=n;i++) t[i]=lower_bound(h+1,h+q+1,t[i])-h; for(i=1;i<=n;i++) { a[i]=d[i]; c[i]=t[i]; } for(i=1;i<=n;i++) a[i]+=a[i-1]; gao(f1); for(i=1;i<=n;i++) a[i]*=2; c[1]=0; gao(g1); reverse(t+2,t+n+1); reverse(d+1,d+n+1); for(i=1;i<=n;i++) { a[i]=d[i]; c[i]=t[i]; } for(i=1;i<=n;i++) a[i]+=a[i-1]; gao(f2); for(i=1;i<=n;i++) a[i]*=2; c[1]=0; gao(g2); int ans=0; for(i=1;i<=n;i++) if(f1[i]<=m) ans=max(ans,i); for(i=1;i<=n;i++) if(f2[i]<=m) ans=max(ans,i); int j; j=n; for(i=1;i<=n;i++) { while(j&&f1[i]+g2[j]>m) j--; if(!j) break; ans=max(ans,i+j-1); } j=n; for(i=1;i<=n;i++) { while(j&&f2[i]+g1[j]>m) j--; if(!j) break; ans=max(ans,i+j-1); } printf("%d\n",ans); return 0; }

【XSY2732】Decalcomania 可持久化線段樹 分治