• AGC023E Inversions


    题解

    考虑总方案数是:

    [f(n)=prod_{i=1}^n (a_i-i+1)\ s.t. forall 1le i<n,a_ile a_{i+1} ]

    我们令 (a_i) 的排名为 (b_i)(c_i) 为排好序后的 (a_i),即:

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

    考虑每两个位置的对于逆序对的贡献,不妨先设这两个位置为 (i)(j)

    (j<i)(a_j<a_i) ,我们求其的逆序对贡献。

    由于是逆序对,所以 (a_i)(a_j) 大的部分可以省去,然后发现就是这样?

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

    这个式子的组合意义实际上就是求出在 (a_i)(a_j) 相同的时候的总方案数,然后除以 (2) ,就是 (p_j>p_i) 的方案数了。

    (i<j)(a_i>a_j) ,我们可以求其的顺序对贡献,用总的减去它。

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

    发现对于前后的,只有一小部分不一样,我们考虑先来处理共同部分:

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

    前面的这个东西对于每一个 (i) 都是一样的,后面的一半可以用线段树来维护,我们考虑从小到大往线段树中添加东西,对于当前位置 (i) ,令其排名为 (k) ,排名比其小的部分已经在线段树中了,我们考虑直接提取出区间和,然后对于全局进行一个区间乘 (frac{c_k-k}{c_k-k+1}) ,同时将位置 (i) 的部分改为 (a_i-b_i) 就可以了吧。

    代码如下

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=2e5+5;
    const int MOD=1e9+7;
    int n;
    struct Data{int data,pos;}a[N];
    bool cmp(const Data a,const Data b){return a.data<b.data;}
    int ksm(int x,int k){
    	int res=1;
    	for(;k;k>>=1,x=x*x%MOD)
    	if(k&1) res=res*x%MOD;
    	return res;
    }
    struct Seg_Tree{
    	struct Node{int data,tag;}tr[N<<2];
    	void up(int u){tr[u].data=(tr[u<<1].data+tr[u<<1|1].data)%MOD;}
    	void updata(int u,int z){
    		tr[u].data*=z,tr[u].data%=MOD;
    		tr[u].tag*=z,tr[u].tag%=MOD;
    	}
    	void down(int u){
    		updata(u<<1,tr[u].tag);
    		updata(u<<1|1,tr[u].tag);
    		tr[u].tag=1;
    	}
    	void build(int u,int l,int r){
    		tr[u].tag=1;
    		if(l==r) return ;
    		int mid=(l+r)>>1;
    		build(u<<1,l,mid);
    		build(u<<1|1,mid+1,r);
    		return ;
    	}
    	int query(int u,int l,int r,int x,int y){
    		if(x>y) return 0;
    		if(x<=l&&r<=y) return tr[u].data;
    		int mid=(l+r)>>1,res=0;down(u);
    		if(x<=mid) res+=query(u<<1,l,mid,x,y);
    		if(y>mid) res+=query(u<<1|1,mid+1,r,x,y);
    		return res%MOD;
    	}
    	void chg(int u,int l,int r,int x,int z){
    		if(l==r) return (void)(tr[u].data=z);
    		int mid=(l+r)>>1;down(u);
    		if(x<=mid) chg(u<<1,l,mid,x,z);
    		else chg(u<<1|1,mid+1,r,x,z);
    		return up(u);
    	}
    }t1;
    struct Tree_Array{
    	int tr[N];
    	int lowbit(int x){return x&(-x);}
    	void add(int k,int x){for(;k<=n;k+=lowbit(k))tr[k]+=x;}
    	int query(int k){int res=0;for(;k;k-=lowbit(k))res+=tr[k];return res;}
    }t2;
    int res=0,tot=1;
    signed main(){
    	cin>>n;
    	for(int i=1;i<=n;++i) scanf("%lld",&a[i].data),a[i].pos=i;
    	sort(a+1,a+1+n,cmp);
    	for(int i=1;i<=n;++i) tot*=a[i].data-i+1,tot%=MOD;
    	for(int i=1;i<=n;++i){
    		int tmp=0;
    		tmp+=t1.query(1,1,n,1,a[i].pos-1),tmp%=MOD;
    		tmp+=MOD-t1.query(1,1,n,a[i].pos+1,n),tmp%=MOD;
    		tmp*=tot*ksm(2*(a[i].data-i+1),MOD-2)%MOD,tmp%=MOD;
    		tmp+=(t2.query(n)-t2.query(a[i].pos))*tot%MOD,tmp%=MOD;
    		// printf("%lld %lld %lld
    ",a[i].data,a[i].pos,tmp);
    		res+=tmp,res%=MOD;
    		t1.updata(1,(a[i].data-i)*ksm(a[i].data-i+1,MOD-2)%MOD);
    		t1.chg(1,1,n,a[i].pos,a[i].data-i),t2.add(a[i].pos,1);
    	}
    	printf("%lld
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    第八篇:Vue组件传参
    第七篇:Vue的路由逻辑跳转
    第六篇:组件数据局部化处理
    第五篇:Vue项目的初始化
    第四篇:Vue的项目开发
    第三篇:Vue指令
    第二篇:Vue实例成员
    第一篇:Vue基础
    第六篇:js对象,类和函数补充
    AngularJS之jeDate日期控件基本使用
  • 原文地址:https://www.cnblogs.com/Point-King/p/14229763.html
Copyright © 2020-2023  润新知