【XSY2732】Decalcomania 可持久化線段樹 分治
題目描述
有一個陶瓷瓶周圍有\(n\)個可以印花的位置。第\(i\)個與第\(i+1\)個位置之間的距離為\(d_i\),在第\(i\)個位置印圖案要\(t_i\)秒。
機器剛開始在\(0\)號位置,可以以\(1\)單位長度每秒的速度移動。
一個位置只能印一個圖案。
現在有\(m\)秒,問你最多能印幾個圖案。
保證時間不足以繞陶瓷瓶一圈。
\(n\leq 100000\)
題解
肯定是先往一邊移動在移動到另外一邊。
不妨設先往右邊移動,那麽右邊的距離就要\(\times 2\)。
求出每邊印\(i\)個印花最少要多少秒。
然後把兩邊合並即可。
考慮怎麽求。
顯然印\(i+1\)
證明:考慮反證法。
設\(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\)
先求出印\(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 可持久化線段樹 分治