1. 程式人生 > >洛谷1081 (NOIp2012) 開車旅行——倍增預處理

洛谷1081 (NOIp2012) 開車旅行——倍增預處理

namespace 一個點 fin 也有 bre fabs new return else

題目:https://www.luogu.org/problemnew/show/P1081

預處理從每個點開始a能走多少、b能走多少。可以像dp一樣從後往前推。

但有X的限制。所以該數組可以變成倍增的樣子。

預處理第一步的找下一個點可以從後往前弄,在樹狀數組上二分。然後正常轉移即可。

樹狀數組上的二分有細節:

  找第一個 f 值為需要的值k的,可以if( query( mid )>=k )ans=mid,r=mid-1;也可以if( query(mid)<=k-1 )ans=mid,l=mid+1; ans++;

倍增數組也有細節,大約就是當目的地為0的時候就不要給距離賦值了。

#include<iostream>
#include
<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int N=1e5+5; const double eps=1e-10,INF=2e9+5; int n,m,h[N],tp[N],f[N],ps[N],S; ll X,dp[N][20][3],mp[N],s[N],ca,cb; double tc=INF; int rdn() { int ret=0,fx=1;char ch=getchar();
while(ch>9||ch<0){if(ch==-)fx=-1;ch=getchar();} while(ch>=0&&ch<=9)(ret*=10)+=ch-0,ch=getchar(); return ret*fx; } ll rdl() { ll ret=0,fx=1;char ch=getchar(); while(ch>9||ch<0){if(ch==-)fx=-1;ch=getchar();} while(ch>=0&&ch<=9)(ret*=10
)+=ch-0,ch=getchar(); return ret*fx; } void add(int x,int k){for(;x<=m;x+=(x&-x))f[x]+=k;} int query(int x){int ret=0;for(;x;x-=(x&-x))ret+=f[x];return ret;} void find(int i,int &ps0,int &ps1) { ps0=0;ps1=0; int k=query(h[i]),l=1,r=m,tp0=0,tp1=0,antp=0; // printf("i=%d k=%d\n",i,k); if(n-i+1>k) { while(l<=r) { int mid=l+r>>1; if(query(mid)<=k)tp0=mid,l=mid+1; else r=mid-1; } tp0++; } if(k>1) { l=1;r=m; while(l<=r) { int mid=l+r>>1; if(query(mid)>=k-1)tp1=mid,r=mid-1; else l=mid+1; } } if(!tp0&&tp1)ps0=ps[tp1],antp=tp1;else if(tp0&&!tp1)ps0=ps[tp0],antp=tp0; else if(tp0&&tp1) {if(tp[h[i]]-tp[tp1]>tp[tp0]-tp[h[i]])ps0=ps[tp0],antp=tp0;else ps0=ps[tp1],antp=tp1;} // printf("tp0=%d tp1=%d ps0=%d\n",tp0,tp1,ps0); if(antp)add(antp,-1); k=query(h[i]);tp0=tp1=0; // printf("i=%d k=%d(antp=%d)\n",i,k,antp); if(n-i>k) { l=1;r=m; while(l<=r) { int mid=l+r>>1; if(query(mid)<=k)tp0=mid,l=mid+1; else r=mid-1; } tp0++; } if(k>1) { l=1;r=m; while(l<=r) { int mid=l+r>>1; if(query(mid)>=k-1)tp1=mid,r=mid-1; else l=mid+1; } } if(!tp0&&tp1)ps1=ps[tp1];else if(tp0&&!tp1)ps1=ps[tp0]; else if(tp0&&tp1){if(tp[h[i]]-tp[tp1]>tp[tp0]-tp[h[i]])ps1=ps[tp0];else ps1=ps[tp1];} // printf("tp0=%d tp1=%d ps1=%d\n",tp0,tp1,ps1); if(antp)add(antp,1); } void cz(int cr) { int a,b;find(cr,b,a); dp[cr][0][2]=a;mp[cr]=b; if(a)dp[cr][0][0]=abs(tp[h[a]]-tp[h[cr]]); int ta=mp[a]; if(ta) { dp[cr][1][2]=ta; dp[cr][1][0]=dp[cr][0][0]; if(ta)dp[cr][1][1]=abs(tp[h[ta]]-tp[h[a]]); } // printf("cr=%d a=%lld b=%lld(a=%d ta=%d)\n",cr,dp[cr][1][0],dp[cr][1][1],a,ta); for(int i=2;i<=17;i++) { a=dp[cr][i-1][2];if(!dp[a][i-1][2])break;// dp[cr][i][0]=dp[cr][i-1][0]+dp[a][i-1][0]; dp[cr][i][1]=dp[cr][i-1][1]+dp[a][i-1][1]; dp[cr][i][2]=dp[a][i-1][2]; } } int main() { n=rdn(); for(int i=1;i<=n;i++)tp[i]=h[i]=rdn(); sort(tp+1,tp+n+1);m=unique(tp+1,tp+n+1)-tp-1; for(int i=n;i;i--) { h[i]=lower_bound(tp+1,tp+n+1,h[i])-tp; add(h[i],1);ps[h[i]]=i;cz(i); } X=rdl();ll yX=X; for(int i=1;i<=n;i++) { // printf("i=%d\n",i); int now=i;ca=0;cb=0;X=yX; for(int j=17;j;j--) // { // if(j<=3)printf("X=%lld now=%d j=%d a=%lld b=%lld\n" // ,X,now,j,dp[now][j][0],dp[now][j][1]); if(dp[now][j][0]+dp[now][j][1]<=X) { X-=dp[now][j][0]+dp[now][j][1]; ca+=dp[now][j][0];cb+=dp[now][j][1]; if(dp[now][j][2])now=dp[now][j][2]; } // } // printf("X=%lld now=%d j=0 a=%lld\n",X,now,dp[now][0][0]); if(now&&dp[now][0][0]<=X)ca+=dp[now][0][0]; // printf("i=%d ca=%lld cb=%lld\n",i,ca,cb); if(cb) { double w=(double)ca/cb; if(w<tc||(fabs(w-tc)<=eps&&h[i]>h[S]))tc=w,S=i; } // printf("i=%d S=%d\n",i,S); } printf("%d\n",S); m=rdn(); while(m--) { S=rdn();X=rdn(); int now=S;ca=0;cb=0; for(int j=17;j;j--) // { // if(j<=3)printf("X=%lld now=%d j=%d a=%lld b=%lld\n" // ,X,now,j,dp[now][j][0],dp[now][j][1]); if(dp[now][j][0]+dp[now][j][1]<=X) { X-=dp[now][j][0]+dp[now][j][1]; ca+=dp[now][j][0];cb+=dp[now][j][1]; if(dp[now][j][2])now=dp[now][j][2]; } // } // printf("X=%lld now=%d j=0 a=%lld\n",X,now,dp[now][0][0]); if(now&&dp[now][0][0]<=X)ca+=dp[now][0][0]; printf("%lld %lld\n",ca,cb); } return 0; }

洛谷1081 (NOIp2012) 開車旅行——倍增預處理