1 条题解

  • 0
    @ 2025-8-24 22:50:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Heptagon18
    Where there is a will, there is a way.

    搬运于2025-08-24 22:50:20,当前版本为作者最后更新于2023-08-21 19:33:42,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Description

    原题链接

    更好的浏览体验


    Solution

    Part 0 - 一些提示

    “对 42949672964294967296 取模”本质上其实就是 unsigned int\text {unsigned int} 自然溢出。

    “以...为峰的序列”看上去性质很难发掘,尝试将它分解成多个简单条件?

    操作没有修改数之间的相对关系。预处理的可能性?

    然后,就没有然后了。

    Part 0.5 - 写在前面

    主要算法:方案数 DP,树状数组优化前缀和。

    感谢 @youyou2007 @英雄趁年少6 帮忙验题!

    Part 1 - 发掘题面信息

    题面首先给出了“峰”的定义。

    考虑将“峰”的定义拆解为“以 asa_s 结尾的严格单调递增序列”与“以 asa_s 开头的严格单调递减序列”,asa_s 处的答案即为这两种序列方案数的乘积。

    以“以 asa_s 结尾的严格单调递增序列”为例,考虑 DP 转移。

    prespre_s 表示以 asa_s 结尾的严格单调递增序列

    容易得到:pres=i=1s1prei  (as>ai)pre_s=\sum\limits_{i=1}^{s-1} pre_i \;(a_s>a_i)

    直接使用该方程转移复杂度是 O(n2)O(n^2) 的。

    基于此,我们可以对于每次询问暴力求解,总时间复杂度 O(qn2)O(qn^2),获得 10%10\% 的分数。

    考虑优化。

    观察条件“as>aia_s>a_i”,发现其本质是一个值域上的前缀和。

    于是可以用树状数组优化。

    严格单调递减序列同理。

    至此,我们完成了预处理的全部工作,时间复杂度 O(nlogamax))O(n\log a_{\max}))

    Part 2 - 观察操作性质

    经过预处理后,我们得到了 prepre 数组(存严格单调递增序列方案数)与 nxtnxt 数组(存严格单调递减序列方案数),考虑一次交换操作对两个数组的影响。

    我们先考虑对 prepre 数组的影响。

    (这里的影响都是考虑不相等的两个数,两个相等的数交换后对答案没有产生任何影响。)

    观察交换操作中的两个数,可以发现,较小数的 prepre 并没有改变。

    感性理解一下,无论较大数处于什么位置,较小数的 prepre 都不可能由较大数的 prepre 转移而来。

    现在我们知道了较小数的 prepre 不变,较大数的 prepre 就好处理了。

    如果 ak<ak+1a_k<a_{k+1},那么在预处理时,prek+1pre_{k+1}prekpre_k 转移过来了,而交换后不能转移,需要将 prek+1pre_{k+1} 减去 prekpre_k

    ak>ak+1a_k>a_{k+1} 同理,将 prekpre_k 加上 prek+1pre_{k+1} 即可。

    考虑剩下的数。

    发现在交换过程中仅有较大数的 prepre 发生变化,则仅有从较大数转移 prepre 值的数会发生变化,即为 i>k+1,ai>max(ak,ak+1)i>k+1,a_i>\max(a_k,a_{k+1}) 的所有数。

    nxtnxt 修改同理。

    于是可以用预处理的思路暴力修改 + 转移,时间复杂度 O(qn)O(qn),可以通过 30%30\% 的数据。

    发现值域较小,可以在值域上修改 + 转移,时间复杂度 O(qamax)O(qa_{\max}),跑不满,可以通过 60%60\% 的数据。

    Part 3 - 优化

    观察最后一个部分分,发现 qq 达到了 10610^6 的级别, O(q×???)O(q\times???) 必然会超时。

    考虑在询问前预处理出全部答案。

    回顾操作性质,发现转移仅和两个要素有关:元素值、位置。

    进一步发现位置仅分为两部分:[1,k1][1,k-1][k+2,n][k+2,n]

    使用扫描线的思想,动态维护这两个区间。

    接着思考,在区间中需要维护什么信息。

    还是以 prepre 数组为例,考虑一次交换操作的贡献。

    首先考虑其它数不互相影响的情况。

    设较大数的 prepre 最后加上了 g1g_1nxtnxt 最后加上了 g2g_2,满足 i[k+2,n],ai>max(ak,ak+1)i\in[k+2,n],a_i>\max(a_k,a_{k+1}) 的集合为 f  (f=m)f\;(|f|=m),满足 i[1,k1],ai>max(ak,ak+1)i\in[1,k-1],a_i>\max(a_k,a_{k+1}) 的集合为 p  (p=o)p\;(|p|=o)

    g1g_1 最终对答案的贡献为 $g_1\times nxt_{f_1}+g_1\times nxt_{f_2}+\cdots+g_1\times nxt_{f_m} = g_1\times(nxt_{f_1}+nxt_{f_2}+\cdots+nxt_{f_m}) = g_1\times \sum\limits_{i=1}^m nxt_{f_i}$。

    再结合上 fi>max(ak,ak+1)f_i>\max(a_k,a_{k+1}),容易发现,维护的是 [k+2,n][k+2,n] 范围内值域上的 nxtnxt 后缀和。

    同理可得,nxtnxt 数组需要维护的是 [1,k1][1,k-1] 范围内值域上的 prepre 后缀和。

    接着考虑其它数相互影响的情况。

    发现刚才的做法有个致命的缺陷:由于区间内仍然存在大小关系,一个数减去的不一定是 gg,而可能是 2g,3g2g,3g 等。

    举个例子,假如当前 [k+2,n][k+2,n] 区间内的数为 [5,8,3,9][5,8,3,9],当前的 max(ak,ak+1)=4\max(a_k,a_{k+1})=4

    则因为 5>45>455prepre 需要减去 gg

    又因为 8>5,48>5,488prepre 需要减去 2g2g

    以此类推。

    观察可以得知,在每个数加进区间时,比它大的所有数均需要多减去一个 gg

    那么我们定义带权权值数组 preypreynxtynxtypreyd,nxtydprey_d,nxty_d 分别表示当取 ada_d 这个数时,总共在两个区间内分别需要减的 pre,nxtpre,nxt 之和。

    可以发现,带权权值对应的即为两个区间内值域上的 prepre 后缀和与 nxtnxt 后缀和。

    那么总共需要减去的就是 $g_1\times\sum\limits_{i=1}^m nxty_{f_i}+g_2\times\sum\limits_{j=1}^o prey_{p_j}$。

    用树状数组动态更新即可。

    时间复杂度 O(nlogamax)O(n\log a_{\max}),总时间复杂度 O(nlogamax+q)O(n\log a_{\max}+q),可以通过本题。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    inline ll read()
    {
        ll x=0,r=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-') r=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*r;
    }
    
    void write(long long t)
    {
        if(t<0)
        {
            putchar('-');
            t=-t;
        }
        if(t>=10)write(t/10);
        putchar(t%10+'0');
    }
    
    long long n,q;
    long long a[1000005];
    unsigned int tree[10005][15];
    unsigned int pre[1000005];
    unsigned int nxt[1000005];
    unsigned int prey[1000005];
    unsigned int nxty[1000005];
    unsigned int del[1000005];
    unsigned int ans[1000005];
    
    long long lowbit(long long t)
    {
        return t&(-t);
    }
    
    void update(long long x,long long k,long long n,long long op)
    {
        for(long long i=x;i<=n;i+=lowbit(i)) 
            tree[i][op]+=k;
    }
    
    unsigned int query(long long t,long long k)
    {
        long long ans=0;
        for(long long i=t;i>=1;i-=lowbit(i)) ans+=tree[i][k];
        return ans;
    }
    
    int Answer(unsigned int ans)
    {
        unsigned int BASE=998244353ll*ans+ans*ans+ans/9991+ans%2159;
        BASE^=9810;
        BASE^=51971;
        BASE=BASE>>7;
        BASE=BASE<<11;
        BASE^=751669;
        BASE^=23465695622566ll;
        return BASE%(n-1)+1;
    }
    
    signed main()
    {
        n=read();
        q=read();
        long long maxx=0;
        for(long long i=1;i<=n;i++)
        {
            a[i]=read();
            maxx=max(maxx,a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            pre[i]=1;
            pre[i]+=query(a[i]-1,0);
            update(a[i],pre[i],maxx,0);
        }
        unsigned int prey_cnt=0;
        unsigned int nxty_cnt=0;
        unsigned int cnt=0;
        for(int i=n;i>=1;i--)
        {
            nxt[i]=1;
            nxt[i]+=query(a[i]-1,1);
            update(a[i],nxt[i],maxx,1);
            cnt+=nxt[i]*pre[i];
        }
        for(int i=1;i<=n;i++)
        {
            ans[i]=cnt;
            prey[i]=prey_cnt-query(a[i],2)+pre[i];
            prey_cnt+=prey[i];
            update(a[i],prey[i],maxx,2);
        }
        for(int i=n;i>=1;i--)
        {
            nxty[i]=nxty_cnt-query(a[i],3)+nxt[i];
            nxty_cnt+=nxty[i];
            update(a[i],nxty[i],maxx,3);
        }
        for(int i=1;i<n;i++)
        {
            if(a[i]<a[i+1])
            {
                del[i]=-query(a[i]-1,4)-1;
                ans[i]+=del[i]*(nxty[i+1]-nxt[i+1]);
            }
            else if(a[i]>a[i+1])
            {
                del[i]=query(a[i+1]-1,4)+1;
                ans[i]+=del[i]*(nxty[i]-nxt[i]);
            }
            update(a[i],pre[i],maxx,4);
        }
        for(int i=n-1;i>=1;i--)
        {
            if(a[i]<a[i+1])
            {
                unsigned int op=query(a[i]-1,5)+1;
                ans[i]+=op*(prey[i+1]-pre[i+1]);
                ans[i]-=pre[i+1]*nxt[i+1]-(pre[i+1]+del[i])*(nxt[i+1]+op);
            }
            else if(a[i]>a[i+1])
            {
                unsigned int op=-query(a[i+1]-1,5)-1;
                ans[i]+=op*(prey[i]-pre[i]);
                ans[i]-=pre[i]*nxt[i]-(pre[i]+del[i])*(nxt[i]+op);
            }
            update(a[i+1],nxt[i+1],maxx,5);
        }
        int k;
        k=read();
        for(int i=1;i<=q;i++)
        {
            write(ans[k]);
            putchar('\n');
            k=Answer(ans[k]);
        }
        return 0;
    }
    
    
    • 1

    信息

    ID
    9122
    时间
    1000ms
    内存
    128MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者