CF2234E Vlad, Misha and Two Arrays 题解
CF2234E Vlad, Misha and Two Arrays
好题。我们从小到大考虑排列中的每一个数,对于区间
假设我们已经找到的最小值被放在哪里,一个直观的想法是把原序列按照这个位置劈开,递归到两边继续做这个过程。我们把左右两边的最小值的位置到这个位置连边,原序列1就被转化为了一棵树。又由于左右两边因为最小值的阻隔互相没有限制,因此唯一的限制是左右两边的值大于中间的值,这就转化为了一个定形态小根堆权值为排列计数问题,可以直接套用 UVA11174 Stand in a Line 的做法。
接下来的问题就是如何求出这棵树的形态了。对于这种分治找到某一个点后原地劈开的问题,有一个经典做法:直接分治顺着扫每个位置是否满足条件显然是
证明的话考虑这个东西的复杂度相当于一棵二叉树,某个点
顺带一提,分治的时候不需要考虑有多个可行位置的情况,因为随便选一个递归下去最后一定会有一段不存在最小值的位置。
#include <bits/stdc++.h>
using namespace std;
long long t,n,a[600000],flag=1,ans=1;
const long long mod=1e9+7;
long long power(long long a,long long p)
{
long long x=a%mod,ans=1;
while(p)
{
if(p&1)ans=ans*x%mod;
p>>=1;
x=x*x%mod;
}
return ans;
}
long long solve(long long l,long long r)
{
if(l>r)return 0;
long long siz=0,flag=0;
for(int i=1;i<=(r-l+2)/2;i++)
{
long long p=l+i-1;
if(a[p]==(p-l+1)*(r-p+1))
{
flag=1,siz=solve(l,p-1)+solve(p+1,r)+1,ans=ans*power(siz,mod-2)%mod;
break;
}
p=r-i+1;
if(a[p]==(p-l+1)*(r-p+1))
{
flag=1,siz=solve(l,p-1)+solve(p+1,r)+1,ans=ans*power(siz,mod-2)%mod;
break;
}
}
if(flag==0)ans=0;
return siz;
}
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
ans=1;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
long long siz=solve(1,n);
for(int i=1;i<=siz;i++)ans=ans*i%mod;
printf("%lld\n",ans);
}
return 0;
}