P1081 [NOIP2012]开车旅行[倍增]

P1081 开车旅行    题面较为啰嗦。大概概括:一个数列,只能从一个点向后走,两种方案:A.走到和自己差的绝对值次小的点B.走到和自己差的绝对值最小点;花费为此差绝对值;若干询问从规定点向后最多花费$X$,且以移动方式A开始每走一次切换一次方式。求以A、B方式各花费多少。


不看题解切紫题一遍过了,兴奋~然而连想带写花了四小时左右,真要在noip考场上怕不是要凉。。。我太菜了QwQ

先看第一问,找比值最小点,实际上就是拆成$N$个询问,扔到第二问的询问里面做掉的说。所以主要看对于询问点怎么向后找终止点。可以猜出应该是$O(logN)$一次询问,所以就要求高效的跳法。

考虑A若干次之后走到后面一个点,从这个点继续走,这样一个状态,很多询问都可能经历。所以对于一个点,希望预处理出以他为起点有关的信息。

维护从他开始的终点?显然不行。

若最远点$T_0$,可能当下到这起点已花了一些代价,由于代价制限导致走不到最远,大概能走到$T_1 < T_0$。也就是说,$S$~$T_1$这一段是我可以走过的,$T_1$之后都到不了。

所以可以根据实际情况二分查找最远点。实现起来,就是个倍增,到目标点要走$k$次,总是可以拆成走二的指数幂次叠加,枚举当前走的$2^i$次,可行就跳,不可行就呆原地。

这个是处理询问的方法。所以瓶颈就在于如何预处理倍增数组。坑死我了。

设计状态$fa[i][j],fb[i][j],to[i][j]$表示从$i$点出发,走$2^j$次,A、B各自花费,以及讫点。

对于底层$to[i][0]$,只采用A方式移动,找出后面次小代价即可。单独处理。

对于次底层$f[i][1]$,开始用AB轮流交替方式移动,所以在底层要顺便处理以B方式移动一次的相关信息,在这一层与A合并。

之后,由于每个状态都是走偶数次的,涉及转移状态都是以A开始的,比如我移动4次,由前2次和后2次拼接,这两段都是以A开始的。所以直接合并即可。

提一下最底层找相邻点的几种方法:

  • 每个点后面离他最近的。。前驱后继?平衡树?考虑用set来替代。找出大于他的两个,小于他的两个(没有则设为0),排一下序处理出来
  • set找4个点同上,然后。。分类讨论。。我的弱智方法。。。。qwq
  • 双向链表。可以将数组排序,将其用数组模拟成链表,之后,和每个点相邻的,虽然不一定是原来数组中他后面的元素,但是对于原数组1号,他在排序数组中相邻的肯定是前驱后继。找出之后将之从链表中删除,再考虑原数组的2号点,这时就不存在1号点的干扰了,后面同理。

常数稍大,但不影响能过,反正都是$O((N+M)logN)$的。相关细节注意一下即可,如超出1e9提前特判掉等等。

  1 #include
  2 #include
  3 #include
  4 #include
  5 #include
  6 #include<set>
  7 #define dis first
  8 #define pos second
  9 #define dbg(x) cerr<<#x<<" = "< 10 #define _dbg(x,y) cerr<<#x<<" = "< 11 using namespace std;
 12 typedef pair<int,int> pii;
 13 typedef long long ll;
 14 templateinline char MIN(T&A,T B){return A>B?A=B,1:0;}
 15 templateinline char MAX(T&A,T B){return A1:0;}
 16 templateinline T _min(T A,T B){return AA:B;}
 17 templateinline T _max(T A,T B){return A>B?A:B;}
 18 namespace io{
 19     const int SIZE = (1 << 21) + 1;
 20     char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
 21     #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
 22     inline void flush (){fwrite (obuf, 1, oS - obuf, stdout);oS = obuf;}
 23     inline void putc (char x){*oS ++ = x;if (oS == oT) flush ();}
 24     template <class I>
 25     inline void read(I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
 26         for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;}
 27     template <class I>
 28     inline void print (I x){
 29         if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while(x) qu[++ qr] = x % 10 + '0',  x /= 10;while (qr) putc (qu[qr--]);}
 30     struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
 31 }
 32 using io::read;
 33 using io::putc;
 34 using io::print;
 35 const int N=100000+7,INF=0x3f3f3f3f;
 36 set s;
 37 int h[N],fa[N][18],fb[N][18],to[N][18],to2[N],disb[N];
 38 int n,Q,T,m;
 39 
 40 inline void preprocess_the_bottom(){
 41     set::iterator up,down;
 42     int d0,d1,d2,d3,p0,p1,p2,p3;
 43     m=__lg(n);
 44     fa[n][0]=fb[n][0]=disb[n]=0;to[n][0]=to2[n]=n+1;s.insert(make_pair(h[n],n));
 45     for(register int i=n-1;i;--i){
 46         down=up=s.lower_bound(make_pair(h[i],0));
 47         d0=d1=d2=d3=0,p0=p1=p2=p3=n+1;
 48         if(down!=s.begin())--down,d0=h[i]-(*down).dis,d0>1e9?(d0=0):(p0=(*down).pos);//下侧第一
 49         if(down!=s.begin())--down,d2=h[i]-(*down).dis,d2>1e9?(d2=0):(p2=(*down).pos);//下侧第二
 50         if(up!=s.end())d1=(*up).dis-h[i],d1>1e9?(d1=0):(p1=(*up).pos);//上侧第一
 51         if(up!=s.end()&&++up!=s.end())d3=(*up).dis-h[i],d3>1e9?(d3=0):(p3=(*up).pos);//上侧第二
 52         if(d0){
 53             if(d1){//两侧都有 
 54                 if(d1<d0){
 55                     disb[i]=d1,to2[i]=p1;
 56                     if(d3&&d30]=d3,to[i][0]=p3;//上侧第二为次近 
 57                     else fa[i][0]=d0,to[i][0]=p0;//下侧第一为次近 
 58                 }
 59                 else{
 60                     disb[i]=d0,to2[i]=p0;
 61                     if(d2&&d2<=d1)fa[i][0]=d2,to[i][0]=p2;//下侧第二为次近 
 62                     else fa[i][0]=d1,to[i][0]=p1;//上侧第一为次近 
 63                 }
 64             }
 65             else{//只有下侧 
 66                 disb[i]=d0,to2[i]=p0;
 67                 fa[i][0]=d2,to[i][0]=p2;
 68             }
 69         }
 70         else disb[i]=d1,to2[i]=p1,fa[i][0]=d3,to[i][0]=p3;//只有上侧或者两侧都没有 
 71 //        dbg(i);_dbg(disb[i],to2[i]);_dbg(fa[i][0],to[i][0]);
 72         s.insert(make_pair(h[i],i));
 73     }
 74     to2[n+1]=n+1;for(register int i=0;i<=m;++i)to[n+1][i]=n+1;
 75 }
 76 
 77 inline void preprocess(){
 78     for(register int i=1;i<=n;++i)fa[i][1]=fa[i][0],fb[i][1]=disb[to[i][0]],to[i][1]=to2[to[i][0]];
 79     for(register int i=2;i<=m;++i){
 80         for(register int j=1;j<=n;++j){
 81             fa[j][i]=fa[j][i-1]+fa[to[j][i-1]][i-1],fb[j][i]=fb[j][i-1]+fb[to[j][i-1]][i-1];to[j][i]=to[to[j][i-1]][i-1];
 82             fa[j][i]+0ll+fb[j][i]>1e9?(fa[j][i]=0,fb[j][i]=0,to[j][i]=n+1):0;
 83         }
 84     }
 85 }
 86 int xa,xb;
 87 inline void Query(int x,int tot){
 88     xa=xb=0;
 89     for(register int i=m;~i;--i)if(to[x][i]<=n&&fa[x][i]+fb[x][i]<=tot)tot-=fa[x][i]+fb[x][i],xa+=fa[x][i],xb+=fb[x][i],x=to[x][i];
 90 }
 91 int x,tot,p,q,ans;
 92 int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
 93     read(n);for(register int i=1;i<=n;++i)read(h[i]);
 94     preprocess_the_bottom();preprocess();
 95     read(Q);Query(1,Q);p=xa,q=xb,ans=1;
 96     for(register int i=2;i<=n;++i){
 97         Query(i,Q);
 98         if(!xb){if(!q)if(h[ans]continue;}
 99         if(xa*1ll*qi;
100         else if(xa*1ll*q==xb*1ll*p)if(h[ans]i;
101     }
102     print(ans);read(T);putc('\n');
103     for(register int i=1;i<=T;++i){
104         read(x),read(tot);Query(x,tot);
105         print(xa),putc(' '),print(xb),putc('\n');
106     }
107     return 0;
108 }

转载于:https://www.cnblogs.com/saigyouji-yuyuko/p/10740308.html

你可能感兴趣的:(P1081 [NOIP2012]开车旅行[倍增])