zoukankan      html  css  js  c++  java
  • 「AGC023E」Inversions【组合计数】

    AGC023E

    Description

    给定一个长度为 (n) 的序列(a),求所有满足 (forall i,P_ile a_i)(1sim n) 的排列的逆序数的和。

    答案对 (10^9+7) 取模。

    (nle 2 imes 10^5,1le a_ile n)

    Solution

    首先,考虑总方案,将 (a) 从大到小排序得到序列 (c) ,记 (b_i)(a_i) 的排名,那么合法的排列方案有:

    [f(n)=prod_{i=1}^{n}(a_i-b_i+1)=prod_{i=1}^{n}(c_i-i+1) ]

    这是因为按照 (a_i) 从小到大依次确定每个 (p_i) 的取值,此时前 (i-1)(p) 都在 (p_i) 的取值范围中,不能选,因此可选的只有 (c_i-i+1) 个。

    考虑分别计算每一对数的贡献。

    对于 (i>j,a_i>a_j),此时 (a_i)(>a_j) 的部分可以忽略掉了,因此可以令 (a_i)(a_j) 相同,此时的所有排列中一定恰好有一半满足 (p_j>p_j),于是用总方案数除 (2) 即可。

    [ans_{i,j}=dfrac{f(n)frac{a_j-b_j}{a_i-b_i+1}prod_{k=b_j+1}^{b_i-1}frac{c_k-k}{c_k-k+1}}{2} ]

    对于 (i<j,a_i> a_j):考虑使用总数减去顺序对的方案数:

    [ans_{i,j}=f(n)-dfrac{f(n)frac{a_j-b_j}{a_i-b_i+1}prod_{k=b_j+1}^{b_i-1}frac{c_k-k}{c_k-k+1}}{2} ]

    (T(k)=prod_{i=1}^{k}dfrac{c_i-i}{c_i-i+1}),暴力求解即可做到 (mathcal O(n^2))

    两种不同的情况处理起来非常麻烦,考虑取出相似的部分:

    [egin{aligned} g(i,j)&=dfrac{f(n)frac{a_j-b_j}{a_i-b_i+1}prod_{k=b_j+1}^{b_i-1}frac{c_k-k}{c_k-k+1}}{2}\ &=dfrac{f(n)T_{b_i-1}}{2(a_i-b_i+1)}dfrac{a_j-b_j}{T_{b_j}}\ end{aligned}\ ans_i=sum_{j}[a_j<a_i]{[i>j]g(i,j)+[i<j](f(n)-g(i,j)} ]

    于是考虑用线段树维护每个 (j)(dfrac{a_j-b_j}{T_{b_j}}),然后按 (a_i) 从小到大加入线段树,加入 (i) 前计算其贡献,就可以容易通过线段树区间求和完成了。复杂度 (mathcal O(nlog n))

    你直接照着这个式子做,会发现始终 (WA) 了几个点,问题出在一开始将区间积转化为前缀积的部分:当某个 (i) 满足 (c_i-i=0) 时,在计算 (prod_{k=l}^{r})(l>i) 时,(c_i-i) 会成为除数,导致原式结果出错。解决方法是

    [g(i,j)=dfrac{f(n)}{2(a_i-b_i+1)}(a_j-b_j)prod_{k=b_j+1}^{b_i-1}dfrac{c_k-k}{c_k-k+1}\ ]

    让每个 (i) 加入线段树时,将全局的值都乘一个 (dfrac{c_{b_i}-b_i}{c_{b_i}-b_i+1}),并将加入点的初值设为 (a_i-b_i) 就能完美解决这一问题了。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    const int mod=1e9+7;
    const int N=2e5+10;
    inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
    inline void inc(int &x,int y){x=add(x,y);}
    inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;}
    inline int ksm(int x,int y){
    	int ret=1;
    	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*ret*x%mod;
    	return ret;
    }
    int n,a[N],b[N],c[N],id[N],f;
    inline bool cmp(int x,int y){return (a[x]^a[y])?a[x]<a[y]:x<y;}
    namespace SGT{
    	#define lc (p<<1)
    	#define rc (p<<1|1)
    	#define mid ((l+r)>>1)
    	pii tr[N<<2];int tag[N<<2];
    	inline pii operator *(const pii &x,const pii &y){
    		return make_pair(x.first+y.first,add(x.second,y.second));
    	}
    	inline pii operator *(const pii &x,const int &y){
    		return make_pair(x.first,1ll*x.second*y%mod);
    	}
    	inline void pushdown(int p){
    		if(tag[p]==1) return;
    		tag[lc]=1ll*tag[lc]*tag[p]%mod;tr[lc]=tr[lc]*tag[p];
    		tag[rc]=1ll*tag[rc]*tag[p]%mod;tr[rc]=tr[rc]*tag[p];
    		tag[p]=1;
    	}
    	inline pii query(int p,int ql,int qr,int l=1,int r=n){
    		if(ql>qr) return make_pair(0,0);
    		if(ql<=l&&r<=qr) return tr[p];
    		pushdown(p);
    		pii ret=make_pair(0,0);
    		if(ql<=mid) ret=ret*query(lc,ql,qr,l,mid);
    		if(qr>mid) ret=ret*query(rc,ql,qr,mid+1,r);
    		return ret;
    	}
    	inline void update(int p,int x,int v,int l=1,int r=n){
    		if(l==r){tr[p]=make_pair(1,v);return ;}
    		pushdown(p);
    		if(x<=mid) update(lc,x,v,l,mid);
    		else update(rc,x,v,mid+1,r);
    		tr[p]=tr[lc]*tr[rc];
    	}
    	inline void build(int p,int l=1,int r=n){
    		tag[p]=1;
    		if(l==r) return ;
    		build(lc,l,mid);build(rc,mid+1,r);
    	}
    }
    using namespace SGT;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]),id[i]=i;
    	sort(id+1,id+n+1,cmp);
    	f=1;
    	for(int i=1;i<=n;++i){
    		b[id[i]]=i,c[i]=a[id[i]];
    		if(c[i]-i+1<=0){puts("0");return 0;}
    		f=1ll*f*(c[i]-i+1)%mod;
    	}
    	int ans=0;
    	build(1);
    	for(int u=1;u<=n;++u){
    		int i=id[u];
    		pii p=query(1,1,i-1),q=query(1,i+1,n);
    		int t=1ll*f*ksm(2ll*(dec(a[i],b[i])+1)%mod,mod-2)%mod;
    		inc(ans,1ll*t*p.second%mod);	
    		inc(ans,dec(1ll*q.first*f%mod,1ll*q.second*t%mod));
    		int v=1ll*(a[i]-b[i])*ksm(a[i]-b[i]+1,mod-2)%mod;
    		tag[1]=1ll*tag[1]*v%mod;
    		tr[1]=tr[1]*v; 
    		update(1,i,dec(a[i],b[i]));
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Android中实现下拉刷新
    Android中Parcelable接口用法
    Android px、dp、sp之间相互转换
    Android平台调用WebService详解
    Android开发之WebService介绍
    Xamarin.Forms XAML控件的公共属性
    构建伪Update服务器工具isr-evilgrade
    Xcode文件名后的字母含义
    设置USB数据监听
    Xamarin.Forms的基本页面和基本视图
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14974670.html
Copyright © 2011-2022 走看看