[ HDU6305 ] RMQ Similar Sequence

        题意:定义一个数列A的\(RMQ(A,l,r)\)为使得\(a_i\)为\(a_l,a_{l+1},a_{l+2}, \cdots ,a_r\)中最大数的最小的i。

        对于两个数列A与B,定义它们RMQ相似当且仅当两数列元素个数相等且对于所有的\(1 \leq l \leq r \leq n\),有\( RMQ(A,l,r)=RMQ(b,l,r)\)。

        现在给你一个正整数数列\(\{a_i\}\),其中\(1 \leq a_i \leq n\),对于实数数列\(\{b_i\}\),其中\(b_i\)为\([0…1]\)中随机的一个实数,当数列A与B  RMQ相似时,B的权值为\(\sum{b_i}\),否则为0,求B权值的期望。(答案对\(10^9 + 7\)取模)(\(1 \leq n \leq 10^6\))

        题解:首先要观察两个序列RMQ相似的条件。当数列A是一个1..n的全排列时,两个序列RMQ相似当且仅当B中元素的大小顺序和A相同。此时答案为\(\frac{n}{2} \cdot \frac{1}{n!}\)(其中\(\frac{n}{2}\)表示的是B中元素之和的期望,\(\frac{1}{n!}\)表示的是B中元素大小顺序满足条件的概率)。

        问题主要是在于当A中含有相同元素的时候的答案。由于不同的A的全排列所对应的满足条件的B是互不相交的(元素间大小关系不同),所以我们要求出与A  RMQ相似的n排列的个数,答案就是这个乘上上面所说的一个排列所对应的答案。接下来找符合条件的全排列(记为C)的个数。

        考虑A的一个区间\([l..r]\),设\(m=RMQ(A,l,r)\),那么我构造的\(c_m\)一定是C中该区间中的最大值,\(c_m\)的值可以确定下来。可以发现,当取任意的\(l \leq l_0 \leq m\)与\(m \leq r_0 \leq r\)的时候,\(RMQ(C,l_0,r_0)=RMQ(A,l_0,r_0)\)肯定是等于m的,也就是说此时m左右两边的数无论大小关系如何都互不影响,那么我们可以任意分配\(m-l\)个数给左边的区间,\(r-m\)个数给右边的区间,方案数为\(C_{r-l}^{m-l}\),然后递归处理左右两个区间,最后再乘上两个区间的答案就好了。

        由于n有\(10^6\),直接DFS会爆栈。不过BFS写也不麻烦。求区间RMQ我直接用线段树来写了(ST表被卡内存),1200ms勉强还是跑过去了。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10,mo=1e9+7;
struct Segment_Tree
{
	int v[maxn];
	int maxpos[maxn<<2];
	int n;
	#define ls (o<<1)
	#define rs ((o<<1)|1)
	#define mid ((l+r)>>1)
	int GetMaxpos(int a,int b)
	{
		if (v[a]==v[b]) return a;
		else if (v[a]>v[b]) return a;
		else return b;
	}
	void BuildTree(int o,int l,int r,int *a)
	{
		if (l==r)
		{
			maxpos[o]=l;
			v[l]=a[l];
			return;
		}
		BuildTree(ls,l,mid,a);
		BuildTree(rs,mid+1,r,a);
		maxpos[o]=GetMaxpos(maxpos[ls],maxpos[rs]);
	}
	int Query(int o,int l,int r,int a,int b)
	{
		if (a<=l && r<=b) return maxpos[o];
		int res=a;
		if (a<=mid) res=GetMaxpos(res,Query(ls,l,mid,a,b));
		if (b> mid) res=GetMaxpos(res,Query(rs,mid+1,r,a,b));
		return res;
	}
	int Query(int l,int r) {return Query(1,1,n,l,r);}
	void Build_Tree(int _n,int *a) {n=_n;BuildTree(1,1,_n,a);}
#undef ls
#undef rs
#undef mid
}T;
long long p[maxn];
long long inv[maxn],invp[maxn];
int a[maxn];
int n;
long long C(int a,int b)
{
	return (p[a]*invp[b]%mo)*invp[a-b]%mo;
}
void Init()
{
	p[0]=1;
	inv[1]=inv[0]=invp[0]=invp[1]=1;
	for (int i=1;i<=1000000;i++) p[i]=p[i-1]*i%mo;
	for (int i=2;i<=1000000;i++) inv[i]=(mo-mo/i)*inv[mo%i]%mo,invp[i]=invp[i-1]*inv[i]%mo;
}
int L[maxn],R[maxn];
long long Solve()
{
	long long res=1;
	int tot=1;
	L[1]=1,R[1]=n;
	for (int i=1;i<=n;i++)
	{
		int l=L[i],r=R[i];
		if (l==r) continue;
		int x=T.Query(l,r);
		res=(res*C(r-l,x-l))%mo;
		if (l<=x-1) L[++tot]=l,R[tot]=x-1;
		if (x+1<=r) L[++tot]=x+1,R[tot]=r;
	}
	return res;
}
int main()
{
	Init();
	int Case;
	scanf("%d",&Case);
	while (Case--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		T.Build_Tree(n,a);
		printf("%lld\n",(Solve()*invp[n-1]%mo)*inv[2]%mo);
	}
	return 0;
}

Leave a Reply

Your email address will not be published.