zoukankan      html  css  js  c++  java
  • [agc23E]Inversions

    Atcoder

    description

    给你(n)({a_i}),你需要求所有满足(p_ile a_i)(1-n)排列的逆序对个数之和模(10^9+7)
    (n le 2 imes10^5)

    sol

    首先考虑一下所有满足要求的排列总数。记(cnt_i)表示有多少个(a_kge i),从大到小填数,方案数就是$$S=prod_{i=1}^ncnt_i-(n-i)$$
    考虑枚举两个位置(i,j),计算满足(p_i>p_j)的排列数。分两种情况讨论:
    (a_ile a_j):这时候如果你让(a_j=a_i)的话方案数也是一样的,所以就强制把(a_j)改成(a_i),计算满足条件的排列数。因为两个位置本质上没有区别,所以(p_i>p_j)的排列数会恰好是合法排列数的一半。而强制把(a_j)改小这个操作相当于是把连续一段的(cnt_i)减一,可以用某种方式来维护一下。
    (a_i>a_j):总排列数减不合法,相当于是要求(p_i<p_j)的方案数。无非是把上面的(i,j)互换了而已,计算方法还是一样的。
    考虑一下怎么把(O(n^2))的枚举优化到(O(nlog n))。记(D_i=frac{cnt_j-1-(n-j)}{cnt_j-(n-j)})。那么枚举一对(i,j),它们的贡献是$$S imesprod_{k=a_i+1}^{a_j}D_k=S imesfrac{prod_{k=1}^{a_j}D_k}{prod_{k=1}^{a_i}D_k}$$
    树状数组以(a_i)为下标,维护(frac{1}{prod_{k=1}^{a_i}D_k})的前缀和即可。
    但是这样有问题。因为(D_i)可能为(0)。我们可以对每个位置,找到它前面出现的第一个(0),显然这个(0)之前的所有(i)都不会有贡献了,而在这个(0)之后到(j)这一段一定没有(0),所以可以维护不含(0)(D_i)前缀积。
    (a_i>a_j)同理,需要额外维护个数以便计算总方案减不合法。

    code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 2e5+5;
    const int mod = 1e9+7;
    int n,a[N],cnt[N],S=1,z[N],st[N],D[N],ID[N],c1[N],c2[N];
    int fastpow(int a,int b){
    	int res=1;
    	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    void mdf(int k,int v){while(k<=n)c1[k]=(c1[k]+v)%mod,++c2[k],k+=k&-k;}
    int qry1(int k){int s=0;while(k)s=(s+c1[k])%mod,k^=k&-k;return s;}
    int qry2(int k){int s=0;while(k)s+=c2[k],k^=k&-k;return s;}
    int main(){
    	n=gi();
    	for (int i=1;i<=n;++i) ++cnt[a[i]=gi()];
    	for (int i=n;i>=1;--i) cnt[i]+=cnt[i+1];
    	for (int i=1;i<=n;++i){
    		cnt[i]-=n-i;
    		if (cnt[i]<=0) return puts("0"),0;
    		S=1ll*S*cnt[i]%mod;
    	}
    	st[0]=D[0]=1;
    	for (int i=1;i<=n;++i){
    		int x=1ll*(cnt[i]-1)*fastpow(cnt[i],mod-2)%mod;
    		if (!x) st[z[i]=z[i-1]+1]=i,D[i]=D[i-1];
    		else z[i]=z[i-1],D[i]=1ll*D[i-1]*x%mod;
    		ID[i]=fastpow(D[i],mod-2);
    	}
    	int inv2=(mod+1)>>1,ans=0;
    	for (int i=1;i<=n;++i){
    		ans=(ans+1ll*(qry1(a[i])-qry1(st[z[a[i]]]-1)+mod)*D[a[i]]%mod*S%mod*inv2)%mod;
    		mdf(a[i],ID[a[i]]);
    	}
    	memset(c1,0,sizeof(c1)),memset(c2,0,sizeof(c2));
    	for (int i=n;i;--i){
    		ans=(ans-1ll*(qry1(a[i]-1)-qry1(st[z[a[i]]]-1)+mod)*D[a[i]]%mod*S%mod*inv2%mod+mod)%mod;
    		ans=(ans+1ll*qry2(a[i]-1)*S)%mod;
    		mdf(a[i],ID[a[i]]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    正则表达式(十四)——找出某一个网页内部的所有的邮箱
    正则表达式(十三)——分组
    正则表达式(十二)——字符串的替换
    正则表达式(十一)——find和lookingAt
    查看隐藏文件夹
    SpringBoot 热部署
    oracle dmp文件泵导入
    python -爬虫-pycrul安装问题
    阿里云https tomcat配置
    jar包下载
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9563729.html
Copyright © 2011-2022 走看看