Description
Chiaki has a sequence A={a1,a2,…,an}. Let RMQ(A,l,r) be the minimum i (l≤i≤r) such that ai is the maximum value in al,al+1,…,ar.
Two sequences A and B are called \textit{RMQ Similar}, if they have the same length n and for every 1≤l≤r≤n, RMQ(A,l,r)=RMQ(B,l,r).
For a given the sequence A={a1,a2,…,an}, define the weight of a sequence B={b1,b2,…,bn} be ∑i=1nbi (i.e. the sum of all elements in B) if sequence B and sequence A are RMQ Similar, or 0 otherwise. If each element of B is a real number chosen independently and uniformly at random between 0 and 1, find the expected weight of B.
Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤106) – the length of the sequence.
The second line contains n integers a1,a2,…,an (1≤ai≤n) denoting the sequence.
It is guaranteed that the sum of all n does not exceed 3×106.
Output
For each test case, output the answer as a value of a rational number modulo 109+7.
Formally, it is guaranteed that under given constraints the probability is always a rational number pqpq(p and q are integer and coprime, q is positive), such that q is not divisible by 1e9+7. Output such integer a between 0 and 1e9+6 that p−aq is divisible by 1e9+7.
Sample Input
3
3
1 2 3
3
1 2 1
5
1 2 3 2 1
Sample Output
250000002
500000004
125000001
Source
2018 Multi-University Training Contest 1
这题现场被卡常了,3e6的nlogn竟然会T,吐槽HDU的评测机
我们发现对于相同的数,RMQ的位置算最左边的那个,相当于对于相同的数,越靠左越大,这样我们可以对A数组进行重新标号,这样可以得到一个和他similar的一个n的排列A
考虑到在0到1之间随机n个数,有数字相等的概率是0,所以可以认为把随机出来的数离散化以后一定是一个n的排列,所以随机到每种排列的概率应该都是1n!1n!
所以我们现在要求和A similar的排列有多少种
我们尝试给出一种similar的递归定义:
对于数列A和B,他们similar当且仅当
1. 记A的最大值位置为p,B的最大值位置为q,则有p=q
2. A的p左边的部分和B的q左边的部分(如果存在)similar
3. A的p右边的部分和B的q右边的部分(如果存在)similar
我们尝试证明这个定义和原定义是等价的:首先如果最大值的位置不一样,那么任何包含最大值的区间RMQ的位置都不一样,肯定不similar;第二,当左边和右边都similar以后,左边的那些区间和右边的那些区间肯定符合原定义,考虑跨过p的那些RMQ,答案肯定是p,所以所有的区间都和原定义相符
这样我们考虑我们生成的排列A,尝试构造一个排列B和他similar,按照A中n的位置把B中n的位置放好,然后我们从B的n-1个数划分成左右两个集合都是合法的,因为不论怎样划分,考虑所有数的相对关系,两边等价于一个排列,这个部分就是一个组合数
如果我们从大到小插入这些数的话,每次我们要找比当前数位置右的最左位置和比当前位置左的最右位置来确定当前的区间,乘上组合数,这样需要一个set,多出一个log会T
考虑从小到大确定每个数对应的区间,我们建一个向前并查集和一个向后并查集,假设当前考虑到i,查找pos(i)+1和pos(i)-1的祖先以确定当前数对应的区间,然后因为之后要处理的数都比当前数打,所以当前数不可能成为后面的数的区间的边界,在两个并查集中分别将pos(i)和pos(i)+1和pos(i)-1连起来就好了,复杂度O(n)O(n)
这样我们已经算出了获得一个合法排列的概率,要算期望权值,考虑到期望的线性性和每个点的期望权值是1212,所以整个序列的期望权值就是概率乘上n2n2
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define LB long double
#define ull unsigned long long
#define x first
#define y second
#define pb push_back
#define pf push_front
#define mp make_pair
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>
#define LOWBIT(x) x & (-x)
const int INF=2e9;
const LL LINF=2e16;
const int magic=348;
const int MOD=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
inline int getint()
{
bool f;char ch;int res;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}
const int MAXN=1e6;
int n;
int a[MAXN+48];
vector<int> v[MAXN+48];
int pos[MAXN+48];
int inv[MAXN+48],finv[MAXN+48],fac[MAXN+48];
inline void init_inv()
{
fac[0]=fac[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
for (register int i=2;i<=MAXN;i++)
{
inv[i]=MOD-((long long)(MOD/i)*inv[MOD%i])%MOD;
fac[i]=(1ll*fac[i-1]*i)%MOD;
finv[i]=(1ll*finv[i-1]*inv[i])%MOD;
}
}
inline int C(int x,int y)
{
int res=fac[x];
res=(1ll*res*finv[y])%MOD;res=(1ll*res*finv[x-y])%MOD;
return res;
}
struct DSU
{
int pre[MAXN+48];
inline void init() {for (register int i=0;i<=n+1;i++) pre[i]=i;}
inline int find_anc(int x) {if (pre[x]!=x) pre[x]=find_anc(pre[x]);return pre[x];}
inline void update(int x,int y) {x=find_anc(x);y=find_anc(y);pre[x]=y;}
}dsu;
int L[MAXN+48],R[MAXN+48];
int main ()
{
//freopen ("a.in","r",stdin);
//freopen ("a.out","w",stdout);
int ca;ca=getint();int i,j,pt;
init_inv();
while (ca--)
{
n=getint();
for (i=1;i<=n;i++) a[i]=getint();
for (i=1;i<=n;i++) v[i].clear();
for (i=n;i>=1;i--) v[a[i]].pb(i);
pt=0;
for (i=1;i<=n;i++)
for (j=0;j<int(v[i].size());j++)
a[v[i][j]]=++pt,pos[pt]=v[i][j];
a[0]=a[n+1]=n+1;
dsu.init();
for (i=1;i<=n;i++)
{
L[i]=dsu.find_anc(pos[i]-1)+1;
dsu.update(pos[i],pos[i]-1);
}
dsu.init();
for (i=1;i<=n;i++)
{
R[i]=dsu.find_anc(pos[i]+1)-1;
dsu.update(pos[i],pos[i]+1);
}
int ans=1;
for (i=1;i<=n;i++) ans=(1ll*ans*C(R[i]-L[i],R[i]-pos[i]))%MOD;
ans=(((1ll*ans*finv[n])%MOD*inv[2])%MOD*n)%MOD;
printf("%d\n",ans);
}
return 0;
}