[AGC023E] Inversions
tag:组合计数,线段树
第一反应是枚举俩位置计算贡献,然后推推式子优化。
首先先考虑一共有多少种合法的排列,把 \(a_i\) 排序后(设为 \(\{b\}\))
\[ans=\prod_{i=1}^nb_i-i+1
\]
枚举两个位置 \(i,\ j(i<j)\),第一反应是令 \(a_i,a_j\) 都为 \(\min(a_i,a_j)\),然后算合法的排列数除以 \(2\)。然而naive了,只有 \(a_i<a_j\) 时是正确的。当 \(a_i>a_j\) 时算出来的是顺序对的个数,所以用总方案数减一下即可。
设 \(a_i<a_j\),二者在 \(b\) 序列中的位置为 \(c_i,\ c_j\),则贡献为:
\[\frac12ans\cdot\frac{a_i-c_i}{a_j-c_j+1}\cdot\prod_{k=c_i+1}^{c_j-1}\frac{b_k-k}{b_k-k+1}
\]
其实就是把原贡献除掉,再乘上新的贡献。\(a_i>a_j\) 时的贡献就是用 \(ans-\)上式。
分两部分 \(ans\cdot\frac1{2(a_j-c_j+1)},\ (a_i-b_i)\cdot\prod_{k=c_i+1}^{c_j-1}\frac{b_k-k}{b_k-k+1}\)。
考虑从小到大加入 \(a_i\),则保证当前的 \(a_i\ge\) 之前加入的 \(a_j\)。
所以对于下标小于 \(i\) 的部分贡献为正,下标大于 \(i\) 的部分贡献为负且都要加上一个 \(ans\)。
于是用以下标为下标的线段树去维护一下区间和就行了,然后再拿个数据结构维护下标大于 \(i\) 的元素个数。
更新的时候全局乘 \(\frac{a_i-b_i}{a_i-b_i+1}\),然后把 \(i\) 处的值改为 \(a_i-b_i\)。(这样做的话,点 \(j\) 的值就正好对应着 \(\prod\cdots\))
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
enum{
MAXN = 1000005,
MOD = 1000000007
};
inline int ksm(int base, int k=MOD-2){
int res = 1;
while(k){
if(k&1)
res = 1ll*res*base%MOD;
base = 1ll*base*base%MOD;
k >>= 1;
}
return res;
}
inline int dec(int a, int b){
a -= b;
if(a<0) a += MOD;
return a;
}
inline int inc(int a, int b){
a += b;
if(a>=MOD) a -= MOD;
return a;
}
inline void iinc(int &a, int b){a = inc(a,b);}
inline void ddec(int &a, int b){a = dec(a,b);}
inline void upd(int &a, long long b){a = (a+b)%MOD;}
int n, fn;
struct ele{
int val, id;
inline bool operator <(const ele &k)const{return val<k.val;}
}a[MAXN];
struct node{
int mul, sum;
#define mul(x) t[x].mul
#define sum(x) t[x].sum
}t[MAXN<<2];
inline int lc(int x){return x<<1;}
inline int rc(int x){return x<<1|1;}
inline void Push_Up(int x){
sum(x) = inc(sum(lc(x)),sum(rc(x)));
}
inline void Mul(int x, int mul){
mul(x) = 1ll*mul(x)*mul%MOD;
sum(x) = 1ll*sum(x)*mul%MOD;
}
inline void Push_Down(int x){
if(mul(x)!=1)
Mul(lc(x),mul(x)),
Mul(rc(x),mul(x)),
mul(x) = 1;
}
void Mul(int x, int head, int tail, int l, int r, int mul){
if(l<=head and tail<=r) return Mul(x,mul);
Push_Down(x);
int mid = head+tail >> 1;
if(l<=mid) Mul(lc(x),head,mid,l,r,mul);
if(mid<r) Mul(rc(x),mid+1,tail,l,r,mul);
Push_Up(x);
}
void Update(int x, int head, int tail, int loc, int k){
if(head==tail) return sum(x)=k, void();
Push_Down(x);
int mid = head+tail >> 1;
if(loc<=mid) Update(lc(x),head,mid,loc,k);
if(mid<loc) Update(rc(x),mid+1,tail,loc,k);
Push_Up(x);
}
int Query(int x, int head, int tail, int l, int r){
if(l<=head and tail<=r) return sum(x);
Push_Down(x);
int mid = head+tail >> 1, res=0;
if(l<=mid) iinc(res,Query(lc(x),head,mid,l,r));
if(mid<r) iinc(res,Query(rc(x),mid+1,tail,l,r));
return res;
}
namespace BIT{
#define lowbit(x) (x&-x)
int t[MAXN];
inline void Add(int x, int k){for(;x<=n;x+=lowbit(x))t[x]+=k;}
inline int Query(int x){int res=0;for(;x;x-=lowbit(x))res+=t[x];return res;}
inline int Query(int l, int r){return Query(r)-Query(l-1);}
#undef lowbit
}
void Build(int x, int head, int tail){
mul(x) = 1;
if(head==tail) return;
int mid = head+tail >> 1;
Build(lc(x),head,mid); Build(rc(x),mid+1,tail);
}
int main(){
Read(n);
for(register int i=1; i<=n; i++) Read(a[i].val), a[i].id = i;
sort(a+1,a+n+1);
fn = 1;
for(register int i=1; i<=n; i++) fn = 1ll*fn*(a[i].val-i+1)%MOD;
Build(1,1,n);
int ans=0;
for(register int i=1; i<=n; i++){
int tmp=0;
if(a[i].id>1) iinc(tmp,Query(1,1,n,1,a[i].id-1));
if(a[i].id<n) ddec(tmp,Query(1,1,n,a[i].id+1,n));
tmp = 1ll*ksm(2ll*(a[i].val-i+1)%MOD)*tmp%MOD;
iinc(tmp,BIT::Query(a[i].id+1,n));
upd(ans,1ll*tmp*fn);
BIT::Add(a[i].id,1);
Mul(1,1ll*(a[i].val-i)*ksm(a[i].val-i+1)%MOD);
Update(1,1,n,a[i].id,a[i].val-i);
}
cout<<ans<<'\n';
return 0;
}