[agc023E]Inversions【题解】【考试】

3 篇文章 0 订阅
1 篇文章 0 订阅

前言

一次考试的中档题

题面

(直接弄了个vjudge)

sol

这道题一开始我求逆序对先是想枚举一个点,然后在去log的查询他前面的比他大的点的合法情况,虽然这是这道题最后的解法,但是未免有些冲动,因为还有很多细节与铺垫的东西没有考虑而直接跳到了最后一步。
但实际上,这也并不是没有指示作用。接下来,就会以这个想法为指引
这道题一个绕不过去的最基本的、现实的问题就是无论怎样都要知道合法方案怎么求。虽然并不难,但这作为一个套路,应该看到这个题就立马写出来,否则这道题连突破口都没有。
求合法方案显然不复杂。有很多种想法。最简单的就是映射集合法。
显然每一个[1-n]的数字都有一个可以放的集合,这个集合就是那些比他大的 ai a i ,每一个数字的集合显然是一个包含集,也就是小的数选择的范围里包含了大的数的选择范围,这样一来,最大的那一个数(也就是n)的集合就是所有大于等于它的 ai a i 的数量,次大的数(也就是n-1)就是所有大于等于他的数的数量减去1,1就是那个最大的数。根据上树推论即乘法原理,合法方案数就是

i=1nData[i](ni) ∏ i = 1 n D a t a [ i ] − ( n − i )

这个是万里长征第一步。
然后,根据上面的想法,就是要暴力的强制一些逆序对出现,然后再求有这个逆序对的方案数就可以了。
这一个方案数实际上就是求合法数量。
如果强制i,j两个位置为逆序对, i<j i < j ,如果 aj>ai a j > a i ,那个大于显然可以换成等于,并且是总方案数的一半,因为会有一半的情况 pi<pj p i < p j ,一半的情况 pi>pj p i > p j
如果 aj<ai a j < a i ,根据上面的经验,在两个相等的时候,先把 ai a i 等于 aj a j 的算出来除以2,然后 [aj+1,a+i] [ a j + 1 , a + i ] 的这一段的所有情况都符合。
这样算出来是n^3,然后分别维护两个前缀积。
令Data[i]=Data[i]-(n-i),首先怎么O(1)求出任意改变ai值的合法方案。
改变一个ai,就会对一段Data数组造成影响。比如把ai改小成aj那么 Data[[aj+1,aj]] D a t a [ [ a j + 1 , a j ] ] 的值都要减一,这个时候就单独维护一个原始的前缀积,叫做A,一个维护每一个减1以后的前缀积,叫做B,用维护的前缀积进行区间查询,拼凑成要求的那一种前缀积。这个时候就是O(1)的计算了。

但是要处理出某一位为0的情况。首先,原前缀积如果出现0,那么最终的答案肯定是0.只考虑减1以后的前缀积,如果算出来某一个Data[i]为0,首先把这一位记成1,然后对于前缀积上的每一个数组开一个loc记录离自己最近的1出现在哪里。这样先通过loc判断查询的这一段区间值是否为0,然后再进行普通的计算。
一个n已经变成了1,另一个n要变成log。
对于枚举到了i,首先我们可以通过 Σ Σ 的数学推导,也可以通过感性理解得知用线段树维护前面比自己大的一个东西,比自己小的一个东西。这个东西究竟是什么?
对于前面一个 aj a j aj<ai a j < a i ,首先在枚举到 aj a j 时,就会默认后边将要用到自己做贡献的是n,于是预先把A的1-aj乘上B的aj+1到n.如果每一个都这样做,那么枚举到ai时,知道那些比自己小的,把B的aj+1到n替换成A的aj+1到n.
这里还有一个除法,这样就意味着处理0的情况。刚才记录了loc,这里就可以查询 [loc[a[i]]+1,a[i]] [ l o c [ a [ i ] ] + 1 , a [ i ] ] 因为超过loc[a[i]]的肯定是0,不超过的肯定不是0
另一种自己推一下吧

code

#include<bits/stdc++.h>
#define LL long long
using namespace std;
static char gc(){
    static char buf[1<<6],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<5,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
    data=0;
    register char ch=0;
    while(ch<'0'||ch>'9')ch=gc();
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch&15);
        ch=gc();
    }
    return;
}
const int _ = 200101,mod = 1e9+ 7;
int  aa[_],n,Data[_],sum1[_],sum2[_],loc[_],LOC[_],qz[_],hz[_],sum[_];
inline int poww(register int ax,register int cc){
    register int ret=1,Base=ax;
    for(;cc;cc>>=1,Base=1LL*Base*Base%mod)if(cc&1)ret=1LL*ret*Base%mod;
    return ret;
}
inline int Que1(register int l,register int r){ return 1LL*sum1[r]*poww(sum1[l-1],mod-2)%mod;}
inline int Que2(register int l,register int r){ return 1LL*sum2[r]*poww(sum2[l-1],mod-2)%mod;}
inline int lowbit(register int k){return k&(-k);}
inline int query1(register int l,register int r){
    if(r<l)return 0;
    register int ret1=0,ret2=0;
    for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+qz[i])%mod;
    for(register int i=r;i;i-=lowbit(i))ret2=(ret2+qz[i])%mod;
    return (ret2-ret1+mod)%mod;
}
inline int query2(register int l,register int r){
    if(r<l)return 0;
    register int ret1=0,ret2=0;
    for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+hz[i])%mod;
    for(register int i=r;i;i-=lowbit(i))ret2=(ret2+hz[i])%mod;
    return (ret2-ret1+mod)%mod;
}
inline int query3(register int l,register int r){
    if(r<l)return 0;
    register int ret1=0,ret2=0;
    for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+sum[i])%mod;
    for(register int i=r;i;i-=lowbit(i))ret2=(ret2+sum[i])%mod;
    return (ret2-ret1+mod)%mod;
}
inline void fix(register int loc,register int zh1,register int zh2){
    for(register int i=loc;i<=n;i+=lowbit(i))qz[i]=(qz[i]+zh1)%mod,hz[i]=(hz[i]+zh2)%mod,sum[i]++;
    return;
}
int main(){
    freopen("belief.in","r",stdin);
    freopen("belief.out","w",stdout);
    read(n);
    for(register int i=1;i<=n;++i)read(aa[i]),aa[i]=aa[i]>n?n:aa[i],++Data[aa[i]];
    for(register int i=n-1;i>=1;--i)Data[i]=Data[i]+Data[i+1];
    for(register int i=1;i<=n;++i)Data[i]=Data[i]-n+i;
    for(register int i=1;i<=n;++i)if(Data[i]<0){cout<<0<<endl;return 0;}
    sum1[0]=sum2[0]=1;
    for(register int i=1;i<=n;++i)sum1[i]=1LL*sum1[i-1]*Data[i] %mod;
    if(!sum1[n]){puts("0");return 0;}
    for(register int i=1;i<=n;++i){
        --Data[i];
        if(Data[i]==0)loc[i]=i,sum2[i]=sum2[i-1];
        else loc[i]=loc[i-1],sum2[i]=1LL*sum2[i-1]*Data[i]%mod;
    }
    LOC[n+1]=n+1;
    for(register int i=n;i>=1;--i)
        if(!Data[i]) LOC[i]=i;
        else LOC[i]=LOC[i+1];
    register int ans=0,ni2=poww(2,mod-2);
    for(register int i=1;i<=n;++i){
        register int f=1LL*query1(loc[aa[i]]+1,aa[i])*poww(Que2(aa[i]+1,n),mod-2)%mod*Que1(aa[i]+1,n)%mod;
        ans=(ans+1LL*f*ni2%mod)%mod;
        f=1LL*query2(aa[i]+1,LOC[aa[i]+1]-1)*poww(Que2(1,aa[i]),mod-2)%mod*Que1(1,aa[i])%mod;
        f=1LL*f*ni2%mod;
        ans=(ans-f+mod)%mod;
        f=query3(aa[i]+1,n);
        ans=(ans+1LL*f*sum1[n]%mod)%mod;
        fix(aa[i],1LL*Que1(1,aa[i])*Que2(aa[i]+1,n)%mod,1LL*Que2(1,aa[i])*Que1(aa[i]+1,n)%mod);
    }
    cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值