给出一个长度为 n n n的序列,每个位置的数值是一个 1 1 1至 9 9 9的整数,考虑一个建二叉树的过程:
对区间 [ l , r ] [l,r] [l,r]建树,选择一个数 i i i满足 l ≤ i ≤ r l\leq i\leq r l≤i≤r,对 [ l , i − 1 ] [l,i-1] [l,i−1]和 [ i + 1 , r ] [i+1,r] [i+1,r]分别建树,令 i i i为这两棵树的父亲,定义 [ i , i − 1 ] [i,i-1] [i,i−1]为空子树。
现在求对 [ 1 , n ] [1,n] [1,n]建树后,任意一个点到根节点的路径上的最大点权和最小,并求此值。
n ≤ 1 0 5 n\leq 10^5 n≤105
考虑到答案不会超过 9 l o g n 9\ log\ n 9 log n。
我们设 F ( x , c ) F(x,c) F(x,c)表示最大的 y y y使得对区间 [ x , y ] [x,y] [x,y]求解不会大于 c c c。
考虑怎么求 F ( x , c ) F(x,c) F(x,c),枚举根节点的权值 j j j,找到最大的 i i i满足 a i = j a_i=j ai=j且 i ≤ F ( x , c − j ) + 1 i\leq F(x,c-j)+1 i≤F(x,c−j)+1,然后 F ( i + 1 , c − j ) F(i+1,c-j) F(i+1,c−j)就可以更新到 F ( x , c ) F(x,c) F(x,c)。
时间复杂度 O ( 81 n l o g n ) O(81n log\ n) O(81nlog n)
#include
#include
#include
#include
#pragma GCC optimize(2)
#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
using namespace std;
typedef long long ll;
const ll N=105e3,K=140,U=141;
char s[N];
int a[N];
int n;
int f[N][U];
int las[N][10];
inline int max(int a,int b)
{return a>b?a:b;}
inline int min(int a,int b)
{return a<b?a:b;}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
fo(i,1,n){
fo(l,1,9)las[i][l]=las[i-1][l];
a[i]=s[i]^48;
las[i][a[i]]=i;
}
fo(l,1,9)las[n+1][l]=las[n+2][l]=las[n][l];
fo(i,0,K)f[n+1][i]=f[n+2][i]=n;
fo(i,1,K)
fo(l,1,n)if(a[l]>i)f[l][i]=l-1;
else {
f[l][i]=max(f[l][i-1],l);
if(f[l][i]==n)continue;
int sj=min(9,i);
fo(j,1,sj)
f[l][i]=max(f[l][i],f[las[f[l][i-j]+1][j]+1][i-j]);
}
int ans=0;
fo(i,0,K)if(f[1][i]>=n){
ans=i;
break;
}
cout<<ans;
}