*题解:P3147 [USACO16OPEN] 262144 P

题目链接

此题弱化版

解析

对于弱化版,设 \(f_{l,r}\) 表示 \([l,r]\) 之间的数可以合成出的数是什么,枚举断点即可轻松转移。

考虑两个三元组 \((l,k,x)\)\((k + 1,r,x)\)。前两个数表示一个区间,第三个数表示这个区间能够合成出来的数,那么两个区间合并之后,形成一个 \((l,r,x + 1)\)。我们先前的做法,是将状态维度设为 \(l\)\(r\),状态值设为 \(x\)。但实际上可以调换一下。设 \(f_{l,x}\) 表示以 \(l\) 为左端点合成出 \(x\) 的区间右端点的后继(即左闭右开)。于是有形如倍增求 LCA 的转移:

\[\large f_{l,x} = f_{f_{l,x - 1},x - 1} \]

时间复杂度 \(O(NV)\)。其中 \(V\) 为值域。对于其具体的值,考虑能合成出的最大数,其对应着 \(N=262144 = 2^{18}\),所有数字均为 \(40\)。每轮都可以和相邻的数字合成使值加一,同时 \(N\) 减半,最多减半 \(\log_2 N=18\) 次,故最多进行 \(18\) 次加一变为 \(58\)。故 \(V=58\)

代码

/*
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 270000,M = 60,L = 19,L2 = 17,mod = 998244353;
int f[N][M];
int a[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		f[i][a[i]] = i + 1;
	}
	int res = 0;
	for(int i=1;i<M;i++){
		for(int j=1;j<=n;j++){
			f[j][i] = max(f[j][i],f[f[j][i - 1]][i - 1]);
			if(f[j][i]){
				res = max(res,i);
			}
		}
	}
	cout<<res;
	return 0;
}

posted @ 2026-05-29 15:00  yutar  阅读(9)  评论(0)    收藏  举报