倍增优化dp

对于一些dp问题,可以用倍增的思想来在nlogn的时间内求出问题的答案

题目1

若没有跑路器,那么直接Floyd做即可

考虑有跑路器的情况,有改变的只是所有距离为2的整数次幂的点的距离

先处理出来两个点之间有没有距离为2的整数次幂的路程,若有,让两个点的距离变成1,然后跑floyd即可

for(int k=1;k<=64;k++){
		for(int i=1;i<=a;i++){
			for(int j=1;j<=a;j++){
				for(int o=1;o<=a;o++){
					if(ok[o][i][k-1]==true&&ok[i][j][k-1]==true){
						ok[o][j][k]=true;//如果左右两段都是true,则可以拼出这个长度
						dis[o][j]=1;
					}
				}
			}
		}
	}

题目2 

不论求哪个子问题,都能发现需要处理出来每个点经过x天后所到达的点,然后枚举x天后的情况,判断合法性更新答案

考虑用二进制来做这样的工作,求出在第i个点,经过2^k天后所到达的点,到达这个点a车开的距离和b车开的距离

要想求出这个问题,需要三个数组

to[i][j][k]//在第j个点,i先开车,经过2^k天后所到达的点
la[i][j][k]//a所经过的距离
lb[i][j][k]//b所经过的距离

先处理出来k=0的情况

for(long long i=a;i>=1;i--){//最近一定在前驱/后继,次近一定在前驱/前驱的前驱/后继/后继的后继
		s.insert(node(h[i],i));
		set<node>::iterator it=s.lower_bound((node(h[i],i)));
		it--;
		long long w1=(*it).w,id1=(*it).id;
		it++;it++;
		long long w2=(*it).w,id2=(*it).id;
		if(id1<0&&id2>a) continue;
		if(h[i]-w1<w2-h[i]||(h[i]-w1==w2-h[i]&&h[id1]<h[id2])){
			to[0][i][0]=id1;
			la[0][i][0]=h[i]-w1;
			it--;it--;it--;
			long long w3=(*it).w,id3=(*it).id;
			if(h[i]-w3<w2-h[i]||(h[i]-w3==w2-h[i]&&h[id3]<h[id2])){
				to[1][i][0]=id3;
				lb[1][i][0]=h[i]-w3;
			}else{
				to[1][i][0]=id2;
				lb[1][i][0]=w2-h[i];
			}
		}else{
			to[0][i][0]=id2;
			la[0][i][0]=w2-h[i];
			it++;
			long long w3=(*it).w,id3=(*it).id;
			if(h[i]-w1<w3-h[i]||(h[i]-w1==w3-h[i]&&h[id1]<h[id3])){
				to[1][i][0]=id1;
				lb[1][i][0]=h[i]-w1;
			}else{
				to[1][i][0]=id3;
				lb[1][i][0]=w3-h[i];
			}
		}
	}

 然后利用倍增来推出其他情况的值

	for(long long j=1;j<=a;j++){//2^1的情况
		to[0][j][1]=to[1][to[0][j][0]][0];
		to[1][j][1]=to[0][to[1][j][0]][0];
		la[0][j][1]=la[0][j][0];
		lb[0][j][1]=lb[1][to[0][j][0]][0];
		la[1][j][1]=la[0][to[1][j][0]][0];
		lb[1][j][1]=lb[1][j][0];
	}
	for(long long i=2;(1<<i)<=a;i++){//其他情况
		for(long long j=1;j<=a;j++){
			to[0][j][i]=to[0][to[0][j][i-1]][i-1];
			la[0][j][i]=la[0][j][i-1]+la[0][to[0][j][i-1]][i-1];
			lb[0][j][i]=lb[0][j][i-1]+lb[0][to[0][j][i-1]][i-1];
			
			to[1][j][i]=to[1][to[1][j][i-1]][i-1];
			la[1][j][i]=la[1][j][i-1]+la[1][to[1][j][i-1]][i-1];
			lb[1][j][i]=lb[1][j][i-1]+lb[1][to[1][j][i-1]][i-1];
		}
	}

知道了所有情况到达的点和a,b两车所行驶的距离,就可以在log的复杂度计算出来每种问题的答案了

问题1:

for(long long i=1;i<=a;i++){
		long long h1=0,h2=0,dis=b,p=i;
		for(long long k=18;k>=0;k--){
			if(la[1][p][k]+lb[1][p][k]<=dis&&to[1][p][k]>0&&to[1][p][k]<=a){
				dis=dis-la[1][p][k]-lb[1][p][k];
				h2+=la[1][p][k];
				h1+=lb[1][p][k];
				p=to[1][p][k];
			}
		}
		if(h2==0) continue;
		double qz=(double)h1/(double)h2;
		
		if(hh>qz||(hh==qz&&h[ans1]<h[i])){
			ans1=i;
			hh=qz;
		}
		
	}

问题2和问题1类似,只用求一次就行了

while(c--){
		long long o,k;
		scanf("%lld%lld",&o,&k);
		long long h2=0,h1=0;
		for(long long ty=18;ty>=0;ty--){
			if(la[1][o][ty]+lb[1][o][ty]<=k&&to[1][o][ty]>0&&to[1][o][ty]<=a){
				k=k-la[1][o][ty]-lb[1][o][ty];
				h2+=la[1][o][ty];
				h1+=lb[1][o][ty];
				o=to[1][o][ty];
			}
		}
		 printf("%lld %lld\n",h1,h2);
	}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值