P1081[NOIP2012提高组]开车旅行

前两天老师还让我们狂做紫题,为什么今天就要求我们对这一道蓝题打暴力qwq

updata : 今天突然看到,这道题也是紫题了qwq

P1081 开车旅行

这道题一看就是dp一类的题,然后就会很顺畅的想到倍增awa

首先,看一下暴力怎么打。

这道题是当年的T4,然后有整整70分的暴力分,这是十分可观的awa。
所以,先想一下暴力。很容易想到先预处理,再模拟。

当然,如果你看不懂或是完全就不想看我写的这依托答辩,你可以直接去想想正解

以下是代码:


#include<bits/stdc++.h>
using namespace std;
int n,m;
long long x,a,b,s;
bool kk=0;
long long h[100005],l[100005],ll[100005];
long long netA[100005],netB[100005];
double Min=2000000009;
int main()
{
    h[0]=0x3f3f3f3f3f3f3f3f;
    
    cin>>n;
    for(int i=1;i<=n;i++)	cin>>h[i];
    for(int i=n-1;i>=1;i--)
    {
        long long mmin=i+1,mmin2=0;
        l[i]=abs(h[i]-h[i+1]);
        for(int j=i+2;j<=n;j++)
        {
            if(l[i]>abs(h[i]-h[j])||(l[i]==abs(h[i]-h[j])&&h[j]<h[mmin]))
            {
                ll[i]=l[i];
                l[i]=abs(h[i]-h[j]);
                mmin2=mmin;
                mmin=j;
            }
            else if(ll[i]==0||ll[i]>abs(h[i]-h[j])||(ll[i]==abs(h[i]-h[j])&&h[j]<h[mmin2]))
            {
                ll[i]=abs(h[i]-h[j]);
                mmin2=j;
            }
        }
        netA[i]=mmin;
        netB[i]=mmin2;
    }
    long long ans=0;
    cin>>x;
    for(int i=1;i<=n;i++)
    {
    	a=b=kk=0;
        long long fl=i;
        while(1)
        {
            if(kk)
            {
                if(a+b+l[fl]>x||!netA[fl])	break;
                b+=l[fl],fl=netA[fl];
            }
            else
            {
                if(a+b+ll[fl]>x||!netB[fl])	break;
                a+=ll[fl],fl=netB[fl];
            }
            kk^=1;
        }
        if(!ans||1.0*a/b-Min<-1e-9||(fabs(1.0*a/b-Min)<=1e-9&&h[ans]<h[i]))
			Min=1.0*a/b,ans=i;
    }
    cout<<ans<<'\n';
    cin>>m;
    while(m--)
	{
        a=b=kk=0;
        cin>>s>>x;
        while(1)
        {
            if(kk)
            {
                if(a+b+l[s]>x||!netA[s])	break;
                b+=l[s],s=netA[s];
        	}
            else
            {
                if(a+b+ll[s]>x||!netB[s])	break;
                a+=ll[s],s=netB[s];
            }
            kk^=1;
        }
		cout<<a<<" "<<b<<'\n';
    }
    return 0;
}

还是很容易看懂的吧awa?

好了,暴力已经打完了,我们已经有了70分,那么我们现在来想一下正解。

所以就开始想正解 $ dp $ :

首先,我们会发现 $ n,m $ 都很大,所以肯定是要预处理一部分内容,然后 $ O(1) $ 或者是 $ O(log n) $ 解答。

然后会发现,$ O(1) $ 是行不通的,因为 $ O(1) $ 一般是把一部分内容离线下来或者是直接对于所有的情况进行解答,然而 $ x $ 是每次给定的,并且范围是 $ 1e9 $ ,且没有共同性,所以不行。

那我们就来想一想别的方法:$ O(log n) $ 的方法。

可以发现,每一个点将会转移到的下一个点是已经固定的,然后还要$ log n $ 转移+找出答案,所以我们就可以很自然的想到倍增。

让我们来想一下怎么设 $ dp $ 状态:

设 $ f[i][j].x $ 为 $ 小A $ 从第 $ i $ 个点开始,向后驾车 $ 2^{j} $ 轮,能到哪里。

设 $ f[i][j].a $ 为$ 小A $ 从第 $ i $ 个点开始,向后驾车 $2^{j} $ 轮,$ 小A $ 开了多远。

设 f[i][j].b 为小A从第i个点开始,向后驾车 \(2^{j}\) 轮,小B开了多远。

插一下数组解释:

$ netA[i] $ : 由 $ i $ 出发, $ 小A $ 驾车,下一个目的地是哪个点。

$ netB[i] $ : 由 $ i $ 出发, $ 小B $ 驾车,下一个目的地是哪个点。

转移显然:

$ f[i][j].x=f[i+f[i][j-1].x][j-1].x; $

$ f[i][j].a=f[i][j-1].a + f[i+f[i][j-1].x][j-1].a; $

$ f[i][j].b=f[i][j-1].b + f[i+f[i][j-1].x][j-1].b; $

但是!!!

有问题吗?

当然有

仔细思考一下,这个转移式子其实是有很大的问题的

其它的都还好,因为都是2的好几次方

但 $ f[i][0] $ 的意思是往后开一轮,这时,下一轮的司机就换成了 $ 小B $ 。

可状态里是都是 $ 小A $ 开始开车 $ qwq $ 。

所以问题大了 $ QAQ $

那么我们可以变一下 dp 定义:

设 $ f[0/1][i][j] $ 为从i这个点出发,向后驾车 $ 2^{j} $ 轮的各种状态

然后 $ [0/1] $ 代表:

$ 0 :小A $ 先开始开车

$ 1 :小B $ 先开始开车

那我们对于第二维等于 $ 0 $ 和 $ 1 $ 时特殊处理一下:


$ f[0][i][0].w=netA[i]; $

$ f[1][i][0].w=netB[i]; $

$ f[0][i][0].a=abs(h[netA[i]]-h[i]); $

$ f[1][i][0].b=abs(h[netB[i]]-h[i]); $


$ f[0][1][1].w=f[1][f[0][i][0].w][0].w;$

$ f[0][1][1].A=f[0][i][0].A+f[1][f[0][i][0].w][0].A;$

$ f[0][1][1].B=f[0][i][0].B+f[1][f[0][i][0].w][0].B;$


对于 $ j>=2 $ ,就只需要处理 $ f[0][i][j] $就可以了awa


$ f[0][i][j].w=f[0][i+f[0][i][j-1].x][j-1].w; $

$ f[0][i][j].a=f[0][i][j-1].a + f[0][i+f[0][i][j-1].w][j-1].a; $

$ f[0][i][j].b=f[0][i][j-1].b + f[0][i+f[0][i][j-1].w][j-1].b; $


这样就好了awa

然后这道题就结束了吗?

显然没有。

你仔细看一下我们打的暴力你就会发现:

我们在处理 $ netA[i] $ 和 $ netB[i] $ 时的复杂度时 $ O(n^2) $ 的,

所以我们也要将这里加速。

这里我的处理方法是倒序枚举,然后利用 $ set $ 查找前驱和后继。

最后:

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,ss,x0,w;
long long x;

long long hh;
double aans=-1;

int lt,nt;
long long lh,nh;
long long h[100005];
struct node{
   long long A,B;
   int w;
}f[2][100005][21];
struct nnode{
   int bh;
   long long h;
}nn;
bool operator<(nnode A,nnode B){
   return A.h<B.h;
}
set<nnode>s;
void init()
{
   h[0]=5e9;h[n+1]=-5e9;
   nn.bh=0;	nn.h=5e9;
   s.insert(nn);	s.insert(nn);
   nn.bh=n+1;	nn.h=-5e9;
   s.insert(nn);	s.insert(nn);
   nn.bh=n;	nn.h=h[n];
   s.insert(nn);
   for(int i=n-1;i>=1;i--)
   {
   	int ga,gb;
   	nn.bh=i;	nn.h=h[i];
   	s.insert(nn);
   	set<nnode>::iterator it=s.lower_bound(nn);
   	it--;
   	lt=(*it).bh;	lh=(*it).h;
   	it++,it++;
   	nt=(*it).bh;	nh=(*it).h;
   	it--;
   	if(abs(nh-h[i])>=abs(h[i]-lh))
   	{
   		gb=lt;
   		it--,it--;
   		if(abs(nh-h[i])>=abs(h[i]-(*it).h))	ga=(*it).bh;
   		else	ga=nt;
   	}
   	else
   	{
   		gb=nt;
   		it++,it++;
   		if(abs((*it).h-h[i])>=abs(h[i]-lh))	ga=lt;
   		else	ga=(*it).bh;
   	}
   	f[0][i][0].w=ga;
   	f[1][i][0].w=gb;
   	f[0][i][0].A=abs(h[ga]-h[i]);
   	f[1][i][0].B=abs(h[gb]-h[i]);
   }
   for(int i=1;i<=n;i++)
   {
   	f[0][i][1].w=f[1][f[0][i][0].w][0].w;
   	f[0][i][1].A=f[0][i][0].A+f[1][f[0][i][0].w][0].A;
   	f[0][i][1].B=f[0][i][0].B+f[1][f[0][i][0].w][0].B;
   }
   for(int j=2;j<=20;j++)
   {
   	for(int i=1;i<=n;i++)
   	{
   		f[0][i][j].w=f[0][f[0][i][j-1].w][j-1].w;
   		f[0][i][j].A=f[0][i][j-1].A+f[0][f[0][i][j-1].w][j-1].A;
   		f[0][i][j].B=f[0][i][j-1].B+f[0][f[0][i][j-1].w][j-1].B;
   	}
   }
}
node cl(int s,long long x)
{
   node now={0,0,0};
   for(int j=20;j>=0;j--)
   {
   	if(f[0][s][j].w&&now.A+now.B+f[0][s][j].A+f[0][s][j].B<=x)
   	{
   		now.A+=f[0][s][j].A;
   		now.B+=f[0][s][j].B;
   		s=f[0][s][j].w;
   	}
   }
   return now;
}
int main()
{
   cin>>n;
   for(int i=1;i<=n;i++)	cin>>h[i];
   init();
   cin>>x0;
   for(int i=1;i<=n;i++)
   {
   	node ans=cl(i,x0);
   	double C=((double)ans.A)/((double)(max(1ll,ans.B)));
   	if(aans==-1)	w=i,hh=h[i],aans=(ans.B==0?5e9:C);
   	else if(ans.B!=0)
   	{
   		if(aans>C)	aans=C,w=i,hh=h[i];
   		else if(aans==C&&h[i]>hh)	w=i,hh=h[i];
   	}
   	else if(aans==5e9&&h[i]>hh)	w=i,hh=h[i];
   }
   cout<<w<<'\n';
   cin>>m;
   for(int i=1;i<=m;i++)
   {
   	cin>>ss>>x;
   	node ans=cl(ss,x);
   	printf("%lld %lld\n",ans.A,ans.B);
   }
   return 0;
}

posted @ 2024-07-10 21:32  YT0104  阅读(18)  评论(0编辑  收藏  举报