[题解]P3147 [USACO16OPEN] 262144 P

P3146 [USACO16OPEN] 248 G(弱化版)

P3147 [USACO16OPEN] 262144 P

我们先考虑区间dp,设\(f[i][j]\)表示\(a[i,j]\)恰好合并成\(1\)个元素后的答案,\(0\)表示无法合并。

那么对于\(k\)使得\(f[i][k]=f[k+1][j]\neq 0\),有转移\(f[i][j]=f[i][k]+1\)

有结论:对于可以合并的区间\([l,r]\),它们能合并出的答案是唯一的。

\(\bf{Proof}\):可以将每个值\(a[i]\)映射到\(b[i]=2^{a[i]}\),合并相当于同指数相加,所以\(b\)的最终合并答案固定为\(2^x\),那么\(a\)的答案固定为\(x\)

根据此结论,只要找到一个\(k\)满足条件,直接更新并break出去就可以,不需要对所有\(k\)\(\max\)

时间复杂度\(O(n^3)\),可以通过弱化版。

点击查看代码
#include<bits/stdc++.h>
#define N 250
using namespace std;
int n,a[N],f[N][N],ans;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=a[i];
	for(int len=1;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			for(int k=i;k<j;k++){
				if(f[i][k]==f[k+1][j]&&f[i][k]){
				    f[i][j]=f[i][k]+1;
				    break;
				}
			}
			ans=max(ans,f[i][j]);
		}
	}
	cout<<ans<<"\n";
	return 0;
}

根据上面的结论,可以推得:对于一个左端点\(l\),如果存在右端点\(r\),使得\(a[l,r]\)可以合并成整数\(k\),那么这个右端点\(r\)是唯一的。又受上面用二进制思考的证明的启发,我们采用倍增。

\(f[i][j]\)表示以\(i\)为左端点合并成\(j\)这个答案的右端点\(+1\)是多少,\(0\)表示不存在右端点。

有转移\(f[i][j]=f[f[i][j-1]][j-1]\),很纯的倍增。

代码中循环跑到了\(58\),是因为答案最大就是\(58\),因为\(262144=2^{18}\),最极端的情况就是所有元素都是\(40\),此时答案是\(40+18=58\)。所以时间复杂度是\(O(n(\log n+V))\)的。

点击查看代码
#include<bits/stdc++.h>
#define N 262150
#define V 60
using namespace std;
int n,a[N],f[N][V],ans;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],f[i][a[i]]=i+1;
	for(int i=2;i<=58;i++){
		for(int j=1;j<=n;j++){
			if(!f[j][i]) f[j][i]=f[f[j][i-1]][i-1];
			if(f[j][i]) ans=i;
		}
	}
	cout<<ans<<"\n";
	return 0;
}
posted @ 2024-11-25 16:50  Sinktank  阅读(7)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.