AtCoder Grand Contest 029 C - Lexicographic constraints

AtCoder Grand Contest 029 C - Lexicographic constraints_第1张图片

题意

给你n个数,每个数ai表示当前第i个字符串的长度。你现在要构造一个最小的字符集大小m使得可以用字符集的字符表示出n个字符串且要求:对于任意的一个构造出来的字符串si满足字典序大小 S i > S i − 1 S_i>S_{i-1} Si>Si1。求m的最小值。

思考历程

我好蠢,竟然以为m是最大为26。
还以为是道水题,直接暴力模拟。
后来发现模拟比较麻烦,且发现理解错题意了。
然后发现可以二分,然后还是直接暴力强模拟即可。

题解

考虑二分答案。
二分答案出来后可以看做是一个m进制。从小到大开始模拟填数,如果当前不能填数就意味着不可行。
那么填数分三种情况:

  • a i < a i + 1 a_iai<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 
#include 
#include 
#include 
#include 
#include 
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);
}

你可能感兴趣的:(atcoder)