AtCoder Grand Contest 023 E - Inversions

Description

给出长度为 \(n\) 序列 \(A_i\),求出所有长度为 \(n\) 的排列 \(P\),满足 \(P_i<=A_i\),求所有满足条件的 \(P\) 的逆序对数之和
题面

Solution

\(c[k]\) 表示 \(A_i>=k\) 的个数,那么对于所有的 \(c[k]>=(n-k+1)\),不满足则不合法
\(c[k]\) 变为 \(c[k]-(n-k),\)总方案就是 \(\Pi c[k]\)
考虑逆序对 \((i,j)\) 的贡献
如果满足 \(A_i<=A_j\) ,那么把 \(A_j\) 变成 \(A_i\),然后 \((i,j)\) 作为逆序对的方案数就是合法排列的方案数除以 \(2\)
\(A_j\) 变成 \(A_i\) 之后,\([A_i+1,A_j]\) 这一个区间的 \(c\) 会减 \(1\),可以维护一个前缀积 \(\frac{c_i-1}{c_i}\) 可以 \(O(1)\) 算出替换后的贡献
那么就可以树状数组维护一下每个数对的贡献了

对于 \(A[i]>A[j]\) 的情况,补集转换一下,总方案-把 \(A_i\) 替换成 \(A_j\) 的方案,然后和上面一样的做一遍就好了

#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
    int f;char c;
    for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}

const int N=2e5+10,mod=1e9+7;
inline int qm(int x,int k){
    int sum=1;
    while(k){
        if(k&1)sum=1ll*sum*x%mod;
        x=1ll*x*x%mod;k>>=1;
    }
    return sum;
}
int n,a[N],c[N],v0[N],R[N],tr[N],S;
inline void add(int x,int t){
    for(int i=x;i<=n;i+=(i&(-i)))tr[i]=(tr[i]+t)%mod;
}
inline int qry(int x){
    int ret=0;
    for(int i=x;i>=1;i-=(i&(-i)))ret=(ret+tr[i])%mod;
    return ret;
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>n;
  for(int i=1;i<=n;i++)gi(a[i]),c[a[i]]++;
  for(int i=n-1;i>=1;i--)c[i]+=c[i+1]-1;
  for(int i=1;i<=n;i++)if(c[i]<=0)return puts("0"),0;
  v0[0]=S=1;
  for(int i=1;i<=n;i++){
      v0[i]=1ll*v0[i-1]*(c[i]>1?c[i]-1:1)%mod*qm(c[i],mod-2)%mod;
      S=1ll*S*c[i]%mod;
  }
  R[n]=n;
  for(int i=n-1;i>=1;i--)R[i]=c[i+1]>1?R[i+1]:i;
  int ans=0,t;
  //A[i]<=A[j]
  for(int i=n;i>=1;i--){
      t=(qry(R[a[i]])-qry(a[i]-1)+mod)%mod;
      ans=(ans+1ll*t*qm(v0[a[i]],mod-2))%mod;
      add(a[i],v0[a[i]]);
  }
  //A[i]>A[j]
  R[1]=1;
  memset(tr,0,sizeof(tr));
  for(int i=2;i<=n;i++)R[i]=c[i]>1?R[i-1]:i;
  for(int i=n;i>=1;i--){
      t=(qry(a[i]-1)-qry(R[a[i]]-1)+mod)%mod;
      ans=(ans-1ll*t*v0[a[i]]%mod+mod)%mod;
      add(a[i],qm(v0[a[i]],mod-2));
  }
  ans=1ll*ans*qm(2,mod-2)%mod;
  memset(tr,0,sizeof(tr));
  for(int i=1;i<=n;i++){
      ans=(1ll*ans+i-1-qry(a[i])+mod)%mod;
      add(a[i],1);
  }
  ans=1ll*ans*S%mod;
  cout<<ans<<endl;
  return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/8977267.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值