CF2234E Vlad, Misha and Two Arrays
CF2234E Vlad, Misha and Two Arrays
不难发现,最小值是很特殊的,所有包含它的区间的最小值都是它。所以我们可以 \(O(n)\) 遍历整个序列,找到满足 \(i \times (n-i+1) = a_i\) 的 \(i\),\(p_i\) 就是最小的。然后我们可以发现,区间 \([1,i-1]\) 和 \([i+1,n]\) 是相互独立的,因为没有以除 \(i\) 外的任何一个值为最小值的区间是可以跨过 \(i\) 的。于是我们就可以递归求解,时间复杂度为 \(O(n^2)\)
考虑优化。我们的时间复杂度瓶颈在于每次最坏都要把当前递归的部分全部遍历一遍。为了避免这样的情况,我们不顺序遍历,而是从两边往中间遍历。这样每个位置被遍历到的次数就是 \(O(logn)\) 的。因为一个位置在它上一个递归区间被遍历后,它当前所在的递归区间一定比上一个减半(要么在左半边要么在右半边)。这样时间复杂度就变成 \(O(nlogn)\) 了
从另一个方面考虑。对于每一个数,一定存在某个区间的最小值是它。那么我们不妨从第一个数开始找哪个区间的最小值是它。满足条件的区间左端点只能是 \(1\),右端点就是 \(a_1\)。对于区间 \([2,a_1]\) 我们递归处理,因为它跟其他区间是独立的。对于 \(a_1\) 后面的,因为以 \(p_1\) 为最小值的区间右端最远只能延申到 \(a_1\),所以 \(p_{a_1+1} < p_1\)。因此满足以 \(p_{a_1+1}\) 为最小值的区间的左端点有 \(a_1+1\) 种选择,右端点有 \(\frac {a_{a_1+1}} {a_1+1}\) 种选择。以此类推不断往后遍历即可。这样做每个位置只会被遍历到一次,因此时间复杂度为 \(O(n)\)
\(code\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5,mod=1e9+7;
int n;
ll ans;
ll a[N],fac[N],inv[N];
inline ll qpow(ll a,ll b)
{
ll res=1;
while (b)
{
if (b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
inline void prework()
{
fac[0]=1;
for (int i=1;i<=5e5;++i) fac[i]=fac[i-1]*i%mod;
inv[500000]=qpow(fac[500000],mod-2);
for (int i=5e5-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(int n,int m)
{
if (m>n) return 0;
if (m==0||m==n) return 1;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void solve(int l,int r)
{
if (l>=r)
{
if (l==r&&a[l]!=1) ans=0;
return;
}
for (int i=l;i<=r;)
{
int L=i-l+1;
if (a[i]%L)
{
ans=0;
return;
}
ll R=a[i]/L;
if (i+R-1>r)
{
ans=0;
return;
}
ans=ans*C(L+R-2,L-1)%mod;
solve(i+1,i+R-1);
i+=R;
}
}
inline void solve()
{
cin>>n;
for (int i=1;i<=n;++i) cin>>a[i];
ans=1;
solve(1,n);
cout<<ans<<endl;
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
ios::sync_with_stdio(false);
prework();
int T;
cin>>T;
while (T--) solve();
return 0;
}
浙公网安备 33010602011771号