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


    题目描述

      有一个陶瓷瓶周围有(n)个可以印花的位置。第(i)个与第(i+1)个位置之间的距离为(d_i),在第(i)个位置印图案要(t_i)秒。

      机器刚开始在(0)号位置,可以以(1)单位长度每秒的速度移动。

      一个位置只能印一个图案。

      现在有(m)秒,问你最多能印几个图案。

      保证时间不足以绕陶瓷瓶一圈。

      (nleq 100000)

    题解

      肯定是先往一边移动在移动到另外一边。

      不妨设先往右边移动,那么右边的距离就要( imes 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(nlog^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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Tarjan在图论中的应用(三)——用Tarjan来求解2-SAT
    【CodeForces】CodeForcesRound594 Div1 解题报告
    JS面向对象组件(三)--面向对象中的常用属性和方法
    JS面向对象组件(二)--Javascript原型链
    JS面向对象组件(一) ---包装对象与原型链
    JS面向对象组件(六) -- 拖拽功能以及组件的延展
    面试题目
    webstorm安装破解版
    面试题整理
    Javascript模块化编程(三):require.js的用法 (转)
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8514572.html
Copyright © 2020-2023  润新知