洛谷题面传送门 & Atcoder 题面传送门

毒瘤 jxd 作业……

首先我们不能直接对所有排列计算贡献对吧,这样复杂度肯定吃不消,因此我们考虑对每两个位置 \(x,y(x<y)\),计算 \(p_x>p_y\) 的排列个数。如何计算呢?我们考虑先求出对于一个固定的 \(a_1,a_2,\cdots,a_n\),如何求出满足 \(p_i\le a_i\) 的排列 \(p\) 的个数,我们考虑将一个 \(a_i\) 看作一个限制,那么我们可以想到将这些限制从小到大排序并按照这样的顺序钦定每个元素的取值。那么当我们钦定某个 \(a_i\) 的限制对应的 \(p_i\) 的取值时,所有满足 \(a_j\le a_i\) 的限制 \(j\) 对应的 \(p_j\) 肯定已经被钦定过了,并且 \(p_j\le a_i\),也就是说假设 \(a_i\) 是第 \(x\) 个被钦定的,那么钦定到 \(i\) 时还有 \(a_i-(x-1)\) 个空位可以选择。因此设 \(b_{1},b_2,\cdots,b_n\) 为 \(a\) 数组排序后的结果,那么

\[C=\prod\limits_{i=1}^n(b_i-i+1)
\]

我们考虑从这个性质入手计算符合要求的排列个数。对于一对 \((i,j)(i<j)\)​​,如果 \(a_i<a_j\)​​,那么我们就计算 \(p_i>p_j\)​​ 的排列个数,不难发现如果我们固定住了 \(p_i=x\)​​,那么这个限制相当于要求 \(p_j\le x-1\)​​,这个统计起来略有点困难,不过可以注意到,如果我们将 \(a_j\)​ 的限制变得跟 \(a_i\)​ 相同,即我们强制规定 \(p_j\le a_i\)​。那么对于任意一个满足 \(p_i<p_j\)​ 的排列 \(p\)​,通过交换 \(p_i,p_j\)​ 我们总能够得到一个 \(p_i>p_j\)​,且符合以上限制的排列,对于 \(p_i<p_j\)​ 的情况也同理,因此我们可以考虑计算出符合题目原来的条件,且满足 \(p_j\le a_i\)​ 的排列个数,并将这个值除以二即可得到 \(p_i>p_j\)​ 的排列个数。考虑如何求出这个东西,我们假设 \(a_i\)​ 在排好序的数列 \(b\)​ 中是 \(r_i\)​ 名,即 \(a_i=b_{r_i}\)​,那么不难发现如果我们将 \(j\)​ 上界调至 \(a_i\)​,会使得 \(j\)​ 排名变为 \(r_i+1\)​,并且使原来排名在 \(r_i+1\sim r_j-1\)​ 中的数的排名提升一格,即

\[C'=C·\dfrac{a_i-r_i}{a_j-r_j+1}·\prod\limits_{k=r_i+1}^{r_j-1}\dfrac{b_k-k}{b_k-k+1}
\]

考虑怎样求解这个东西,我们按照排名 \(r_i\)​​ 从小到大枚举 \(a_i\)​​,即按照 \(b_1,b_2,\cdots,b_n\)​​ 的顺序枚举这些限制,在访问过程中可以维护一个序列 \(c\)​​,当我们访问到一个 \(i\)​​ 时就令 \(c_i=b_i-i\)​​,然后每次访问完 \(i\)​​ 就将 \(c_1,c_2,\cdots,c_{i-1}\)​​ 全部乘上 \(\dfrac{b_i-i}{b_i-i+1}\)​​,那么可以发现,如果我们记 \(N(x,y)\)​​ 表示当 \(r_i=x,r_j=y\)​​ 时上式中 \(C’\)​​ 的值,那么 \(N(x,y)\)​​ 就等于,我们访问到 \(x\)​​ 时,\(C·\dfrac{1}{b_y-y+1}·c_x\)​​ 的值。故设 \(p_i\)​​ 满足 \(p_{r_i}=i\)​​,那么访问到 \(y\)​​ 时的贡献就是 \(\prod\limits_{p_x<p_y}N(x,y)=C·\dfrac{1}{b_y-y+1}·\sum\limits_{p_x<p_y}c_x\)​,这个可以通过维护一个全局乘+单点更新+区间求和的线段树解决。

当然这仅仅只是 \(a_i<a_j\) 的 \((i,j)\) 的贡献。对于 \(a_i\ge a_j\) 的情况也大同小异,我们考虑拿总排列数 \(C\) 减去 \(p_i<p_j\) 的排列个数,后者只需把序列翻转过来再做一遍上述操作即可。

时间复杂度 \(n\log n\)​​。

注:以下代码中 \(a_i\ge a_j\) 部分的处理方法与题解略有差异,它并没有按照题解所述将序列翻转过来重复 \(a_i<a_j\) 部分的过程,而是在第一遍扫描线过程中一并算出 \(a_i\ge a_j\) 的答案。

const int MAXN=2e5;
const int MOD=1e9+7;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n;
struct data{
int val,id;
data(int _val=0,int _id=0):val(_val),id(_id){}
bool operator <(const data &rhs){return val<rhs.val;}
} a[MAXN+5];
struct node{int l,r,val,lz;} s[MAXN*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].lz=1;if(l==r) return s[k].val=0,void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(s[k].lz^1){
s[k<<1].val=1ll*s[k<<1].val*s[k].lz%MOD;
s[k<<1].lz=1ll*s[k<<1].lz*s[k].lz%MOD;
s[k<<1|1].val=1ll*s[k<<1|1].val*s[k].lz%MOD;
s[k<<1|1].lz=1ll*s[k<<1|1].lz*s[k].lz%MOD;
s[k].lz=1;
}
}
void tag_mul(int x){
s[1].lz=1ll*s[1].lz*x%MOD;
s[1].val=1ll*s[1].val*x%MOD;
}
int query(int k,int l,int r){
if(l>r) return 0;
if(l<=s[k].l&&s[k].r<=r) return s[k].val;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%MOD;
}
void modify(int k,int p,int x){
if(s[k].l==s[k].r) return s[k].val=x,void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
(p<=mid)?modify(k<<1,p,x):modify(k<<1|1,p,x);
s[k].val=(s[k<<1].val+s[k<<1|1].val)%MOD;
}
int t[MAXN+5];
void add(int x,int v){for(int i=x;i;i&=(i-1)) t[i]+=v;}
int query(int x){int ret=0;for(int i=x;i<=n;i+=(i&(-i))) ret+=t[i];return ret;}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].id=i;
sort(a+1,a+n+1);int tot=1,res=0;
for(int i=1;i<=n;i++) tot=1ll*tot*(a[i].val-i+1)%MOD;
if(!tot) return puts("0"),0;build(1,1,n);
for(int i=1;i<=n;i++){
res=(res+1ll*qpow(2*(a[i].val-i+1),MOD-2)*query(1,1,a[i].id-1))%MOD;
res=(0ll+res+query(a[i].id)-1ll*qpow(2*(a[i].val-i+1),MOD-2)*query(1,a[i].id+1,n)%MOD+MOD)%MOD;
tag_mul(1ll*(a[i].val-i)*qpow(a[i].val-i+1,MOD-2)%MOD);
modify(1,a[i].id,a[i].val-i);add(a[i].id,1);
} printf("%d\n",1ll*res*tot%MOD);
return 0;
}

Atcoder Grand Contest 023 E - Inversions(线段树+扫描线)的更多相关文章

  1. AtCoder Grand Contest 023 E - Inversions

    Description 给出长度为 \(n\) 序列 \(A_i\),求出所有长度为 \(n\) 的排列 \(P\),满足 \(P_i<=A_i\),求所有满足条件的 \(P\) 的逆序对数之和 ...

  2. AtCoder Grand Contest 001 C Shorten Diameter 树的直径知识

    链接:http://agc001.contest.atcoder.jp/tasks/agc001_c 题解(官方): We use the following well-known fact abou ...

  3. AtCoder Grand Contest 023 A - Zero-Sum Ranges

    Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...

  4. Atcoder Grand Contest 023

    A 略 B 略 C(计数) 题意: 有n个白球排成一行,故有n-1个空隙,我可以给一个空隙对应的两个白球都涂黑.n-1个空隙的一个排列就对应着一个涂黑顺序,定义这个涂黑顺序的价值是“将所有n个球都涂黑 ...

  5. AtCoder Grand Contest 023 C - Painting Machines

    Description 一个长度为 \(n\) 的序列,初始都为 \(0\),你需要求出一个长度为 \(n-1\) 的排列 \(P\), 按照 \(1\) 到 \(n\) 的顺序,每次把 \(P_i\ ...

  6. AtCoder Grand Contest 023 F - 01 on Tree

    Description 题面 Solution HNOI-day2-t2 复制上去,删点东西,即可 \(AC\) #include<bits/stdc++.h> using namespa ...

  7. AtCoder Grand Contest 011

    AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...

  8. AtCoder Grand Contest 012

    AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...

  9. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

随机推荐

  1. 初学python-day4 字典(已更新完)

  2. Spring Cloud Gateway GatewayFilter的使用

    Spring Cloud Gateway GatewayFilter的使用 一.GatewayFilter的作用 二.Spring Cloud Gateway内置的 GatewayFilter 1.A ...

  3. 用cmd命令行创建vue项目模板

    1.进入cmd命令行 输入存放项目的位置 2.通过vue create 项目名称 创建项目 3.选择Manually select features 4.通过空格选中第1.2.5.6.7.去掉8 4. ...

  4. 深入理解Linux C语言内存管理

    问题不能拖,我这就来学习一下吧,争取一次搞定. 在任何程序设计环境及语言中,内存管理都十分重要. 内存管理的基本概念 分析C语言内存的分布先从Linux下可执行的C程序入手.现在有一个简单的C源程序h ...

  5. 修改记事本PE结构弹计算器Shellcode

    目录 修改记事本PE结构弹计算器Shellcode 0x00 前言 0x01 添加新节 修改节数量 节表位置 添加新节表信息 0x02 添加弹计算器Shellcode 修改代码 0x03 修改入口点 ...

  6. Linux下软链接与硬链接的区别

    由于下面会说到inode,所以如果没有了解过,请务必搞懂inode的真正含义,厚颜无耻的推荐我的一篇博客:Linux磁盘与文件系统管理 如果我们在系统中新建一个文件,我们看到的文件名实际上只是表面现象 ...

  7. Java测试开发--lambda函数式编程(六)

    1.Lambda 表达式,是jdk1.8特性,接口里只有一个方法. 举例说明 // ()参数列表 ->连接符 {方法体} 经常在匿名对象 testPerson(()->{System.ou ...

  8. SpringBoot 中发布ApplicationEventPublisher,监听ApplicationEvent 异步操作

    有这么一个业务场景:当用户注册后,发送邮件到其邮箱提示用户进行账号激活,且注册成功的同时需要赠送新人用户体验卡券. 业务有了,那么问题也就来了. What? 问题....问题?我听说你有问题? 来拔刀 ...

  9. idea查看方法在哪里被调用

    方法一 选中方法名,右键选择Find Usages 方法二 选中方法快捷键ctrl + alt + h查看Hierarchy Callers

  10. C++ STL的一些应用

    STL一些应用 记录一些STL算法在开发中用得比较舒服的情况(不断添加...) lower_bound(begin,end,val)算法 算法说明 查找>=val的第一个元素,如果没有,返回en ...