CF1809F Traveling in Berland - 倍增 -

发布时间 2023-03-25 17:36:30作者: SkyRainWind

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

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

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