HDU6305: RMQ Similar Sequence 题解

这篇博客介绍了如何解决HDU6305问题,即找到与给定序列RMQ相似的序列的期望权重。通过分析RMQ相似性的递归定义,博主提出了使用并查集的方法来高效地计算在0到1之间随机选取n个数并离散化后的排列,与原序列相似的排列的概率。最终,博主展示了如何计算期望权重,并给出了样例输入和输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值