5995. 【WC2019模拟2019.1.12】二分的代价

二分的代价

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 lir,对 [ l , i − 1 ] [l,i-1] [l,i1] [ i + 1 , r ] [i+1,r] [i+1,r]分别建树,令 i i i为这两棵树的父亲,定义 [ i , i − 1 ] [i,i-1] [i,i1]为空子树。
现在求对 [ 1 , n ] [1,n] [1,n]建树后,任意一个点到根节点的路径上的最大点权和最小,并求此值。

Data Constraints

n ≤ 1 0 5 n\leq 10^5 n105

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 iF(x,cj)+1,然后 F ( i + 1 , c − j ) F(i+1,c-j) F(i+1,cj)就可以更新到 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
#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;
}

你可能感兴趣的:(动态规划)