二分的代价
Description
给出一个长度为
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]建树后,任意一个点到根节点的路径上的最大点权和最小,并求此值。
Data Constraints
n ≤ 1 0 5 n\leq 10^5 n≤105
Solution
考虑到答案不会超过
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)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#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;
}