[思维构造] 题解 Lexicographic constraints

[思维构造] 题解 Lexicographic constraints

题目链接

题目分析

答案显然有可二分性,考虑二分答案 (k) ,然后判断 (k) 是否合法。

假如 (k) 合法并且我们构造出来了所有的 (S_1,S_2,dots,S_n) ,如果将所有的字符串插入 Trie 树,那么所有节点的儿子数量必然小于等于 (k) 并且存在一种 ( m dfs) 方式使得每个字符串对应的 ( m dfs) 序严格递减。

为了满足条件,我们肯定尽可能地希望 (S_i)(S_{i+1}) 在 Trie 树上的距离尽可能地小,因为这样才可以尽可能地满足条件,所以我们可以直接建出 Trie 树,然后贪心地把下一个字符串拼接上来。

但是 Trie 树的高度可能会很大,不过可以使用建虚树的方法,只记录最右链中儿子数量非一的点的深度和儿子数量即可。

参考代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;
template<typename T>void read(T&x){
	static char c;static int f;
	for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
	static char q[65];int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}
const int maxn=200005;
int n,a[maxn],st0[maxn],st1[maxn];
int judge(int mid){
	int tp=0;
	++tp;st0[tp]=0;st1[tp]=1;
	++tp;st0[tp]=a[1];st1[tp]=0;
	for(int i=2;i<=n;++i){
		int ok=false;
		while(st0[tp]>=a[i])--tp,ok=true;
		if(ok&&st0[tp]!=a[i]-1){
			++tp;st0[tp]=a[i]-1;st1[tp]=1;
		}
		int no=st0[tp];
		while(~no&&st0[tp]==no&&st1[tp]==mid)--tp,--no;
		if(~no){
			if(st0[tp]!=no){
				++tp;st0[tp]=no;st1[tp]=1;
			}
			++st1[tp];
			++tp;st0[tp]=a[i];st1[tp]=0;
		}
		else
			return false;
	}
	return true;
}
int main(){
	read(n);int ok=true;
	for(int i=1;i<=n;++i){
		read(a[i]);
		if(i>1)ok&=(a[i]>a[i-1]);
	}
	if(ok)return puts("1"),0;
	int l=2,r=n;
	while(l<r){
		int mid=(l+r)>>1;
		if(judge(mid))r=mid;
		else l=mid+1;
	}
	write(l),pc('
');
	return 0;
}

原文地址:https://www.cnblogs.com/lsq147/p/13930174.html