【XSY2732】Decalcomania 可持久化线段树 分治

题目描述

  有一个陶瓷瓶周围有\(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;
}
posted @ 2018-03-06 15:26  ywwyww  阅读(245)  评论(0编辑  收藏  举报