题意
给你n个数,每个数ai表示当前第i个字符串的长度。你现在要构造一个最小的字符集大小m使得可以用字符集的字符表示出n个字符串且要求:对于任意的一个构造出来的字符串si满足字典序大小 S i > S i − 1 S_i>S_{i-1} Si>Si−1。求m的最小值。
思考历程
我好蠢,竟然以为m是最大为26。
还以为是道水题,直接暴力模拟。
后来发现模拟比较麻烦,且发现理解错题意了。
然后发现可以二分,然后还是直接暴力强模拟即可。
题解
考虑二分答案。
二分答案出来后可以看做是一个m进制。从小到大开始模拟填数,如果当前不能填数就意味着不可行。
那么填数分三种情况:
- a i < a i + 1 a_i<a_{i+1} ai<ai+1直接在上一个字符串后面填上若干个0即可
- a i = a i + 1 a_i=a_{i+1} ai=ai+1在上一个字符串最后面的位置+1
- a i > a i + 1 a_i>a_{i+1} ai>ai+1把上一个字符串的后面的位置剪掉,剪成一个长度和 a i + 1 a_{i+1} ai+1相等的字符串后做第二步。
当然,如果+1之后大于二分的答案之后就进位即可。
然后我们发现直接维护字符串大小会炸。那么由于很多的1状态是没用的,所以去掉这些1的状态后只剩下那些>1的状态。开个栈维护一下即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <bitset>
using namespace std;
const int maxn=200010;
int n,top,a[maxn];
struct node{
int w,a;
}zhan[maxn];
bool bz;
void doit(int p,int q)
{
if (p==0)
{
bz=false;
return;
}
while (zhan[top].w>p)
{
top--;
}
if (zhan[top].w==p) zhan[top].a++;
else
{
top++;
zhan[top].w=p;
zhan[top].a=2;
}
if (zhan[top].a==q+1)
{
if (top>0)
{
top--;
doit(p-1,q);
}
}
}
bool pd(int x)
{
top=0;zhan[0].w=0;
bz=true;
for (int i=1;i<=n;i++)
{
if (a[i]<=a[i-1])
{
doit(a[i],x);
}
}
return bz;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int l=2;
int r=100000000;
int ans=0;
while (l<=r)
{
int mid=(l+r)/2;
if (pd(mid))
{
ans=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
int k=0;
for (int i=1;i<n;i++)
{
if (a[i]>=a[i+1])
{
k=1;
}
}
if (k==0) ans=1;
printf("%d\n",ans);
}