[AGC023E] Inversions

考虑合法序列的方案数,将 ai 排序为 ciaic 中的位置为 bi,答案为 i=1n(cii+1),我们记为 tot

逆序对考虑拆成每两个位置 (i,j) 之间的贡献,考虑有多少情况对于 i<j 使得 pi>pj

对于 j<i,aj<ai,贡献为:

tot×ajbjaibi+1k=bj+1bi1ckkckk+12

这个柿子的含义是,我们将 ai:=aj,然后重新排列 a 后的合法序列数量,由于 pi,pj 互相换了序列依然合法,且我们只需要 pi>pj 的答案,所以再除以 2

同时对于 i<j,aj<ai,我们计算顺序对的贡献,然后用总方案数减去即可,也就是:

tottot×ajbjaibi+1k=bj+1bi1ckkckk+12

我们稍微化简一下最上面的柿子:(下面的一样)

tot2(aibi+1)×(ajbj)×k=bj+1bi1ckkckk+1

按照 bi 从小到大遍历 i,用线段树维护每一个 j 对应的答案即可,复杂度 O(nlogn)

#include<bits/stdc++.h> 
using namespace std;
#define N 200005
#define ls u<<1
#define rs u<<1|1
#define p 1000000007
#define ll long long
int n,a[N],b[N],c[N],d[N];
ll tr[N<<2],tag[N<<2],all=1,ans;
inline ll qpow(ll a,int b){ll ans=1;for(;b;b>>=1,a=a*a%p)if(b&1)ans=ans*a%p;return ans;}
inline void pushtag(int u,ll x){tr[u]=tr[u]*x%p,tag[u]=tag[u]*x%p;}
inline void push(int u){if(tag[u]!=1)pushtag(ls,tag[u]),pushtag(rs,tag[u]),tag[u]=1;}
inline void pull(int u){tr[u]=(tr[ls]+tr[rs])%p;}
void build(int u,int l,int r){
	tr[u]=0,tag[u]=1;
	if(l==r)return;
	int m=(l+r)>>1;
	build(ls,l,m);build(rs,m+1,r);
}
void upd(int u,int l,int r,int x,ll y){
	if(l==r)return tr[u]=y,void();
	int m=(l+r)>>1;push(u);
	if(m>=x)upd(ls,l,m,x,y);
	else upd(rs,m+1,r,x,y);
	pull(u);
} 
ll qry(int u,int l,int r,int L,int R){
	if(L<=l&&r<=R)return tr[u];
	int m=(l+r)>>1;push(u);ll ans=0;
	if(m>=L)ans=(ans+qry(ls,l,m,L,R))%p;
	if(m<R) ans=(ans+qry(rs,m+1,r,L,R))%p;
	return ans;
}
int t[N];
inline void add(int x){for(int i=x;i<=n;i+=(i&-i))++t[i];}
inline int  ask(int x){int ans=0;for(int i=x;i;i-=(i&-i))ans+=t[i];return ans;}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),d[i]=i;
	sort(d+1,d+1+n,[](const int x,const int y){return a[x]<a[y];});
	for(int i=1;i<=n;i++)b[d[i]]=i,c[i]=a[d[i]];
	for(int i=1;i<=n;i++)all=all*(c[i]-i+1)%p;
	build(1,1,n);
	for(int k=1;k<=n;k++){
		int i=d[k];
		ans=(ans+qry(1,1,n,1,i)*all%p*qpow(2*(a[i]-b[i]+1),p-2)%p)%p;
		ans=(ans+(ask(n)-ask(i))*all%p-qry(1,1,n,i,n)*all%p*qpow(2*(a[i]-b[i]+1),p-2)%p+p)%p;
		pushtag(1,(c[k]-k)*qpow(c[k]-k+1,p-2)%p);
		upd(1,1,n,i,a[i]-b[i]);
		add(i);
	}
	printf("%lld",ans);
}
posted @   xcyyyyyy  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示