P1081-开车旅行【倍增,链表,dp】
正題
題目大意:https://www.luogu.org/problemnew/show/P1081
題目大意
有若干個城市有不同的海拔hhh,兩個城市之間的距離定義為∣hx?hy∣|h_x-h_y|∣hx??hy?∣
小A每次走次近的,小B每次走最近的。它們輪流開車。且只會往編號更大的城市開。
問一:在距離≤X\leq X≤X的情況下求一個起點是的他們兩個開的距離之和最小。
問二:若干個Si,XiS_i,X_iSi?,Xi?求在SiS_iSi?出發,距離≤Xi\leq X_i≤Xi?時他們各開多遠。
解題思路
首先鏈表(或平衡樹)預處理出gaiga_igai?和gbigb_igbi?表示在第iii個城市小AAA或小BBB下一個會到的城市。
然后考慮dpdpdp
設fi,j,0/1f_{i,j,0/1}fi,j,0/1?表示從jjj出發走了2i2^i2i次,到小A/小B開車,到達的地點。
設dai,j,0/1da_{i,j,0/1}dai,j,0/1?表示從jjj出發走了2i2^i2i次,到小A/小B開車,小AAA走了多遠。
設dbi,j,0/1db_{i,j,0/1}dbi,j,0/1?表示從jjj出發走了2i2^i2i次,到小A/小B開車,小BBB走了多遠。
然后首先
f0,j,0=gaj,f0,j,1=gbjf_{0,j,0}=ga_j,f_{0,j,1}=gb_jf0,j,0?=gaj?,f0,j,1?=gbj?
da0,j,0=dist(j,gaj),da0,j,1=0da_{0,j,0}=dist(j,ga_{j}),da_{0,j,1}=0da0,j,0?=dist(j,gaj?),da0,j,1?=0
db0,j,1=dist(j,gbj),db0,j,0=0db_{0,j,1}=dist(j,gb_{j}),db_{0,j,0}=0db0,j,1?=dist(j,gbj?),db0,j,0?=0
然后考慮轉移
當i=1i=1i=1時
fi,j,k=fi?1,fi?1,j,k,kxor1f_{i,j,k}=f_{i-1,f_{i-1,j,k},k\ xor\ 1}fi,j,k?=fi?1,fi?1,j,k?,k?xor?1?
dai,j,k=dai?1,j,k+dai?1,fi?1,j,k,kxor1da_{i,j,k}=da_{i-1,j,k}+da_{i-1,f_{i-1,j,k},k\ xor\ 1}dai,j,k?=dai?1,j,k?+dai?1,fi?1,j,k?,k?xor?1?
dbi,j,k=dbi?1,j,k+dbi?1,fi?1,j,k,kxor1db_{i,j,k}=db_{i-1,j,k}+db_{i-1,f_{i-1,j,k},k\ xor\ 1}dbi,j,k?=dbi?1,j,k?+dbi?1,fi?1,j,k?,k?xor?1?
但是因為i>1i>1i>1時2i2^i2i依舊是偶數,說以kkk不需要改變
fi,j,k=fi?1,fi?1,j,k,kf_{i,j,k}=f_{i-1,f_{i-1,j,k},k}fi,j,k?=fi?1,fi?1,j,k?,k?
dai,j,k=dai?1,j,k+dai?1,fi?1,j,k,kda_{i,j,k}=da_{i-1,j,k}+da_{i-1,f_{i-1,j,k},k}dai,j,k?=dai?1,j,k?+dai?1,fi?1,j,k?,k?
dbi,j,k=dbi?1,j,k+dbi?1,fi?1,j,k,kdb_{i,j,k}=db_{i-1,j,k}+db_{i-1,f_{i-1,j,k},k}dbi,j,k?=dbi?1,j,k?+dbi?1,fi?1,j,k?,k?
處理好之后,我們定義函數calc(s,x)calc(s,x)calc(s,x)表示
從sss出發,且距離不超過xxx時小AAA和小BBB的行駛距離。
首先我們要設定now,la,lbnow,la,lbnow,la,lb表示現在在哪個點和小AAA和小BBB的行駛距離。
然后當la+lb+dai,now,0+dbi,now,0≤xla+lb+da_{i,now,0}+db_{i,now,0}\leq xla+lb+dai,now,0?+dbi,now,0?≤x時就證明可以行走,那就對應修改now,la,lbnow,la,lbnow,la,lb的值。
codecodecode
#include<cstdio> #include<algorithm> #include<cmath> #include<set> #define ll long long using namespace std; const ll N=101000; struct Node{ll h,id,prev,next; }l[N]; set<int> BST; double ans; ll n,m,mark,la,lb,t; ll h[N],f[21][N][2],da[21][N][2]; ll ga[N],gb[N],db[21][N][2],pre[N]; bool left(ll x){if(!l[x].prev) return 0;if(!l[x].next) return 1;return l[x].h-l[l[x].prev].h<=l[l[x].next].h-l[x].h; } ll cmps(ll a,ll b,ll x){if(!a) return l[b].id;if(!b) return l[a].id;if(l[x].h-l[a].h<=l[b].h-l[x].h) return l[a].id;return l[b].id; } bool cmp(Node x,Node y) {return x.h<y.h;} void Part1() {for(ll i=1;i<=n;i++)l[i].h=h[i],l[i].id=i;sort(l+1,l+1+n,cmp);for(ll i=1;i<=n;i++)pre[l[i].id]=i;for(ll i=1;i<=n;i++)l[i].prev=i-1,l[i].next=i+1;l[1].prev=l[n].next=0;for(ll i=1;i<=n;i++){ll j=pre[i];if(left(j)) gb[i]=l[l[j].prev].id,ga[i]=cmps(l[l[j].prev].prev,l[j].next,j);else gb[i]=l[l[j].next].id,ga[i]=cmps(l[j].prev,l[l[j].next].next,j);l[l[j].prev].next=l[j].next;l[l[j].next].prev=l[j].prev; } } ll dist(ll x,ll y) {return abs(h[x]-h[y]);} void Part2() {for(ll j=1;j<=n;j++){if(ga[j]){f[0][j][0]=ga[j];da[0][j][0]=dist(j,ga[j]);db[0][j][0]=0;}if(gb[j]){f[0][j][1]=gb[j];da[0][j][1]=0;db[0][j][1]=dist(j,gb[j]);}}ll L=0;for(ll i=1;i<=t;i++)for(ll j=1;j<=n;j++)for(ll k=0;k<2;k++){if(i==1) L=k^1;else L=k;f[i][j][k]=f[i-1][f[i-1][j][k]][L];da[i][j][k]=da[i-1][j][k]+da[i-1][f[i-1][j][k]][L];db[i][j][k]=db[i-1][j][k]+db[i-1][f[i-1][j][k]][L];} } void calc(ll s,ll x) {ll now=s;la=lb=0;for(ll i=t;i>=0;i--){if(f[i][now][0]&&la+lb+da[i][now][0]+db[i][now][0]<=x){la+=da[i][now][0];lb+=db[i][now][0];now=f[i][now][0];if(!now) break;}} } int main() {scanf("%lld",&n);t=(int)(log(n*1.0)/log(2.0)+0.001);for(ll i=1;i<=n;i++)scanf("%lld",&h[i]);Part1();Part2();ll X;scanf("%lld",&X);ans=2147483647;for(ll i=1;i<=n;i++){calc(i,X);if(lb&&((double)la/lb)<ans) ans=(double)la/lb,mark=i;}printf("%lld\n",mark);scanf("%lld",&m);for(ll i=1;i<=m;i++){ll s;scanf("%lld%lld",&s,&X);calc(s,X);printf("%lld %lld\n",la,lb);} }總結
以上是生活随笔為你收集整理的P1081-开车旅行【倍增,链表,dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 100个好听到爆的网名 个性昵称推荐
- 下一篇: POJ1275-Cashier Empl