CF1809F Traveling in Berland - 倍增 -

题目链接:https://codeforces.com/contest/1809/problem/F

题解:
对一个点,考虑怎样在 O(logn) 的时间复杂度内求出答案,联想到倍增
但是,倍增合并的时候只能在两个状态相同的情况下合并,但是如果我们直接从 ii+1 这样算的话,每次剩余的油量是不同的,很难合并。
考虑如果将每次剩余油量都设为 0 的话,就比较容易合并了。
如果当前位置 bi=2,那么直接付清 2×ai 的钱,然后移动到下一个
如果当前位置 bi=1,且往后第一个不是 2,那么付清 ai 的钱,然后移动到下一个
否则,比如说有 k 个连续的 2,考虑二分出第一个 sum[l..r]kr,显然在 bi 处先加满油最优,那么到了 r 处可能刚好够用,也可能需要再买一些。如果需要再买一些的话,显然就是 2×(sum[l..r]k) 的额外价格。
这样一来,我们就求得了 dist[i][0] 代表 i 往后移动 20 次的距离,已经此时的价格,倍增即可

对于每个 i,查询的时候就倍增往后跳,注意可能出现没跳完的情况,即某一次 i 即使是 dist[i][0] 也 < 剩余的距离(这是有可能的,因为我们每次 dist 都是取的最大值),容易发现此时当前位置一定为 bi=1,直接求出二者的距离,在 bi=1 处买即可

细节很多,调吐了

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 4e5+5;

int n,k;
int a[maxn], b[maxn];
ll dist[maxn][20], cost[maxn][20], sum[maxn];

int md(int x){
	if(x <= n)return x;
	return x-n;
}

void solve(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]), a[i+n] = a[i];
	for(int i=1;i<=n;i++)scanf("%d",&b[i]), b[i+n] = b[i];
	
	sum[0] = 0;
	for(int i=1;i<=2*n;i++)sum[i] = sum[i-1] + a[md(i)];
	
	int cnt = 0;
	
	for(int i=2*n;i>=1;i--){
		if(b[i] == 2){
			cost[i][0] = 2*a[i];
			dist[i][0] = 1;
		}else if(cnt == 0){
			cost[i][0] = a[i];
			dist[i][0] = 1;
		}else{
			int to = lower_bound(sum+i, sum+i+cnt+1, sum[i-1] + k) - sum;
			if(to == i+cnt+1){
				cost[i][0] = sum[i+cnt] - sum[i-1];
				dist[i][0] = cnt+1;
			}else{
				int delt = to - i;
				if(sum[to] == sum[i-1] + k){
					cost[i][0] = k;
					dist[i][0] = delt+1;
				}else{
					cost[i][0] = k + 2 * (sum[to] - sum[i-1] - k);
					dist[i][0] = delt+1;
				}
			}
		}
		if(b[i] == 2)++ cnt;
		else cnt = 0;
	}
	
	for(int j=1;j<=18;j++)
		for(int i=1;i<=n;i++){
			dist[i][j] = dist[i][j-1] + dist[md(i + dist[i][j-1])][j-1];
			cost[i][j] = cost[i][j-1] + cost[md(i + dist[i][j-1])][j-1];
		}
	
	for(int i=1;i<=n;i++){
		ll r = 0, now = i;
		int tot = 0;
		for(int j=18;j>=0;j--){
			if(tot+dist[now][j] <= n)
				r += cost[now][j],
				tot += dist[now][j],
				now = md(now + dist[now][j]);
		}
		if(tot < n){
			r += sum[i+n-1] - sum[i+tot-1];
		}
		printf("%lld ",r);
	}
	puts("");
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}

posted @   SkyRainWind  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示