[思维构造] 题解 Lexicographic constraints
[思维构造] 题解 Lexicographic constraints
题目分析
答案显然有可二分性,考虑二分答案 \(k\) ,然后判断 \(k\) 是否合法。
假如 \(k\) 合法并且我们构造出来了所有的 \(S_1,S_2,\dots,S_n\) ,如果将所有的字符串插入 Trie 树,那么所有节点的儿子数量必然小于等于 \(k\) 并且存在一种 \(\rm dfs\) 方式使得每个字符串对应的 \(\rm 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('\n');
return 0;
}