P16902
前言
双倍经验:CF2234E
分完治一定要 return 啊!!!
题解
观察这个数组
1 4 1:此时A_2 作为最小值覆盖的区间为4 ,容易发现它一定是A 整个序列中的最小值。1 6 1 2此时A_2 作为最小值覆盖的区间为6 ,容易发现它一定是A 整个序列中的最小值。3 3 3此时\sum B_i 为9 ,而长度为3 的序列的区间总数为\frac{3(3+1)}{2}=10 ,此时一定无解。
所以我们就得到了本题的一些基本性质:
- 当
\sum B_i \neq \frac{n(n+1)}{2} 时无解。 - 当
B_i=i(n-i+1) 时,A_i 为A 整个序列中的最小值。这个很好证明,因为当A_i 为A 整个序列中的最小值时,包含它的所有区间的最小值一定都是它,而区间总数即前面选i 个起点,后面选n-i+1 个终点,即i(n-i+1) 。并且,这个结论可以推广到任意一个区间[l,r] ,当a_q=(q-l+1)(r-q+1) 时,它是[l,r] 的最小值。
那我们知道了这个最小值
与 CF2234E 不同的是这题可以不为排列,而且保证有解。
那我们怎么填数呢?我们知道,这道题中最值分治是从小的逐渐搜到大的,且
当
那么我们就剩下了最后一个问题:如何快速地找到这个最小值呢?请注意:最值分治并没有保证分治出的两个区间长度相等,也就是说,会有可能出现最小值永远偏向一边的情况,如果你遍历区间
所以我们使用一个双指针来搜:
这个东西均摊下来复杂度是 真的不是我不会严谨证明
所以我们就做完了这个题,我是不会告诉你我分治完没 return 的。
::::success[Code]
//风が私を呼んでいる
#include<bits/stdc++.h>
#define FastIO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
#define I using
#define AK namespace
#define CSPS2026 std
I AK CSPS2026;
const int maxn=1e6+10,maxm=1e3+10,mod=1e9+7;
int t,n,m,x,y,z,u,v,w,arr[maxn],res[maxn];
void split(int l,int r,int cur)
{
if(l>r)return;
if(l==r)
{
if(arr[l]>1)res[l]=cur-1;
else res[l]=cur;
}
int i=l,j=r;
for(int tot=l;tot<=r;tot++)
{
if(tot&1)
{
if(arr[i]==(i-l+1)*(r-i+1))
{
res[i]=cur;
split(l,i-1,cur+1);
split(i+1,r,cur+1);
return;
}
if(arr[i]>(i-l+1)*(r-i+1))
{
res[i]=cur-1;
split(l,i-1,cur);
split(i+1,r,cur);
return;
}
i++;
}
else
{
if(arr[j]==(j-l+1)*(r-j+1))
{
res[j]=cur;
split(l,j-1,cur+1);
split(j+1,r,cur+1);
return;
}
if(arr[j]>(j-l+1)*(r-j+1))
{
res[j]=cur-1;
split(l,j-1,cur);
split(j+1,r,cur);
return;
}
j--;
}
}
return;
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
FastIO;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>arr[i];
u+=arr[i];
}
// if(u!=((n*(n+1))>>1))
split(1,n,1);
for(int i=1;i<=n;i++)cout<<res[i]<<" ";
return 0;
}
/*
出好的题!
覆知盖点广识,题着切有目实合的际景背,解较比然自法。
出给题赞点人!
更的要据重是数正本基确,符一合好道的本题标准基!
*/
::::