题意:给定一个排列,其中有可能有一些未确定的数,求出所有可能的排列的排名之和

首先我们要知道怎么算一个给定排列的排名,设它为$p_{1\cdots n}$

排名即为比它小的排列数$+1$,对于每一个比$p$小的排列$s$,我们都能找到$i$使得对于$j\lt i$有$p_j=s_j$且$p_i\gt s_i$,枚举这个$i$,$s_i$可以是任何$\lt p_i$且不在$p_{1\cdots i-1}$中的数,有$p_i-1-\sum\limits_{j\lt i}[p_j\lt p_i]$种选择,后面的$n-i$个数可以用$p_{i+1\cdots n}$随意重排得到,于是$p$的排名为$1+\sum\limits_{i=1}^n(n-i)!\left(p_i-1-\sum\limits_{j\lt i}[p_j\lt p_i]\right)$(所以求一个排列的排名可以用树状数组做到$O(n\log n)$)

现在来做这道题,枚举每一个可能的排列$s$计算答案,为了方便,以下的下标和排列都是$0\cdots n-1$,设$p$中有$k$位未确定,则有$k!$个不同的$s$

$\begin{aligned}&\quad\sum\limits_s1+\sum\limits_{i=0}^{n-1}(n-1-i)!\left(s_i-\sum\limits_{j\lt i}[s_j\lt s_i]\right)\\&=k!+\sum\limits_{i=0}^{n-1}(n-1-i)\sum\limits_s\left(s_i-\sum\limits_{j\lt i}[s_j\lt s_i]\right)\end{aligned}$

前面的部分和$\sum\limits_ss_i$都是很好算的,现在我们要算$\sum\limits_{j\lt i}\sum\limits_s[s_j\lt s_i]$,分类讨论一下

1.$p_i\neq0$,$\sum\limits_ss_i=k!p_i$

1-1.$p_j\neq0$,答案为$k!\sum\limits_{j\lt i}[p_j\lt p_i,p_j\neq 0]$,用树状数组算即可

1-2.$p_j=0$,$s_j$可以从$x\notin p,x\lt p_i$中的数任选,预处理$s_i=\sum\limits_{j\leq i}[p_i=0],np_i=\sum\limits_{j\leq i}[j\notin p]$,答案即为$(k-1)!np_{p_i}\cdot s_i$

2.$p_i=0$,$\sum\limits_ss_i=(k-1)!\sum\limits_{i\notin p}i$

2-1.$p_j\neq0$,$s_i$可以从$x\notin p,x\gt p_j$中的数任选,预处理$sn_i=\sum\limits_{j\leq i}[p_j\neq0]np_{p_j}$,答案为$(k-1)!((i-s_{i-1})np_n-sn_i)$

2-2.$p_j=0$,$s_i,s_j$在满足$s_j\lt s_i$的条件下任选,所以答案为$(s_i-1)\binom k2(k-2)!$

总时间复杂度$O(n\log n)$

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1000000007;
int mul(int a,int b){return(ll)a*b%mod;}
int ad(int a,int b){return(a+b)%mod;}
void inc(int&a,int b){(a+=b)%=mod;}
int p[500010],rp[500010],s[500010],fac[500010],np[500010],l[500010],sn[500010];
//s[i] = '-1's in 0...i
//np[i] = count x [x<=i,x not in p]
//l[i] = count j [j<i,pj<pi,pj!=-1]
//sn[i]= sum [j<=i,pj!=-1] np[pj]
int tr[500010],n;
int lowbit(int x){return x&-x;}
void modify(int x){
	while(x<=n){
		tr[x]++;
		x+=lowbit(x);
	}
}
int query(int x){
	int s=0;
	while(x){
		s+=tr[x];
		x-=lowbit(x);
	}
	return s;
}
int C2(ll n){return n*(n-1)/2%mod;}
int main(){
	int k,i,ans,tmp,snp;
	scanf("%d",&n);
	k=0;
	for(i=0;i<n;i++){
		np[i]=1;
		rp[i]=l[i]=-1;
	}
	for(i=0;i<n;i++){
		scanf("%d",p+i);
		p[i]--;
		if(p[i]==-1)
			k++;
		else{
			np[p[i]]=0;
			rp[p[i]]=i;
		}
		s[i]=k;
	}
	for(i=0;i<n;i++){
		if(~rp[i]){
			l[rp[i]]=query(rp[i]+1);
			modify(rp[i]+1);
		}
	}
	l[0]=0;
	for(i=1;i<n;i++){
		if(l[i]==-1)l[i]=l[i-1];
	}
	snp=0;
	for(i=0;i<n;i++){
		if(np[i])inc(snp,i);
	}
	for(i=1;i<=n;i++)np[i]+=np[i-1];
	fac[0]=1;
	for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	for(i=0;i<n;i++){
		if(i)sn[i]=sn[i-1];
		if(~p[i])inc(sn[i],np[p[i]]);
	}
	ans=0;
	for(i=0;i<n;i++){
		if(~p[i]){
			tmp=mul(fac[k],l[i]);
			if(k)inc(tmp,mul(mul(np[p[i]],fac[k-1]),s[i]));
			inc(ans,mul(fac[n-1-i],mul(fac[k],p[i])-tmp));
		}else{
			if(i)
				tmp=mul(fac[k-1],mul(i-s[i-1],np[n])-sn[i]);
			else
				tmp=0;
			if(s[i]>1)inc(tmp,mul(s[i]-1,mul(C2(k),fac[k-2])));
			inc(ans,mul(fac[n-1-i],mul(snp,fac[k-1])-tmp));
		}
	}
	inc(ans,fac[k]);
	printf("%d",ad(ans,mod));
}

[CODE FESTIVAL 2016]Encyclopedia of Permutations的更多相关文章

  1. 【AtCoder】CODE FESTIVAL 2016 qual C

    CODE FESTIVAL 2016 qual C A - CF -- #include <bits/stdc++.h> #define fi first #define se secon ...

  2. 【AtCoder】CODE FESTIVAL 2016 qual A

    CODE FESTIVAL 2016 qual A A - CODEFESTIVAL 2016 -- #include <bits/stdc++.h> #define fi first # ...

  3. 【AtCoder】CODE FESTIVAL 2016 qual B

    CODE FESTIVAL 2016 qual B A - Signboard -- #include <bits/stdc++.h> #define fi first #define s ...

  4. Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution

    Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution 题目链接:https://atcoder.jp/contests/cf16- ...

  5. Atcoder CODE FESTIVAL 2016 qual C 的E题 Encyclopedia of Permutations

    题意: 对于一个长度为n的排列P,如果P在所有长度为n的排列中,按照字典序排列后,在第s位,则P的value为s 现在给出一个长度为n的排列P,P有一些位置确定了,另外一些位置为0,表示不确定. 现在 ...

  6. CODE FESTIVAL 2016 qualA Grid and Integers

    划年代久远的水 题意 有一个R*C的棋盘,要求在每个格子上填一个非负数,使得对任意一个2*2的正方形区域,左上角和右下角的数字之和等于左下角和右上角的数字之和.有一些格子已经被填上了数字,问现在能否满 ...

  7. [CODE FESTIVAL 2016]Problem on Tree

    题意:给一棵树,对于一个满足以下要求的序列$v_{1\cdots m}$,求最大的$m$ 对$\forall1\leq i\lt m$,路径$(v_i,v_{i+1})$不包含$v$中除了$v_i,v ...

  8. [CODE FESTIVAL 2016]Distance Pairs

    题意:有一个未知的边权为$1$的图,给定所有点到$1$的最短路$a_i$和到$2$的最短路$b_i$,问是否存在这样的图,如果存在,问图中最少有多少条边 先考虑$a_i$,有$a_1=0,a_i\ne ...

  9. CODE FESTIVAL 2016 Grand Final 题解

    传送门 越学觉得自己越蠢--这场除了\(A\)之外一道都不会-- \(A\) 贪心从左往右扫,能匹配就匹配就好了 //quming #include<bits/stdc++.h> #def ...

随机推荐

  1. POJ 2456 Aggressive cows ( 二分搜索)

    题目链接 Description Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The ...

  2. vim 以16进制进行文件编辑

    用 vim中二进制文件的编辑是先通过外部程序xxd来把文件dump成其二进制的文本形式,然后就可以按通常的编辑方式对文件进行编辑,编辑完成后再用xxd 转化为原来的形式即可. 可分如下几步进行: (1 ...

  3. C语言的小括号----其实是逗号运算符

    比如下面的代码: #include <stdio.h> void fun() { int a, b, c, d; a = (, b = ); c = (, ); d = (, ); pri ...

  4. Oracle 内存顾问

    --查看内存相关参数SYS@ test10g> col name for a30SYS@ test10g> col value for a20SYS@ test10g> select ...

  5. NFS+inotify实时同步

    Inotify简介 Inotify是一种文件系统事件通告机制,能够实时监控文件系统下文件的访问.修改.删除等各种变化情况并将其作为事件通告给用户态应用程序.Linux内核从2.6.13版本后已经集成了 ...

  6. 调用微信JS-SDK接口上传图片

    最近要在微信上做个问卷调查,有个上传图片功能,折腾找了半天资料,都不好弄,最终打算调用微信提供的上传图片接口,实现上传图片功能!此功能最大的好处是可以在微信服务器上暂存图片,减少本地服务器图片的缓存, ...

  7. Leetcode 之Binary Tree Postorder Traversal(47)

    中序遍历二叉搜索树,得到的是一个有序的结果,找出其中逆序的地方就可以了.如果逆序的地方相邻,只需把逆序的相换即可:如果不相邻,则需要找到第二个逆序对的 第二个元素再做交换. 定义两个指针p和q来指定需 ...

  8. 16:django 有条件的视图处理(Last-Modified和ETag)&&加密签名

    有条件的视图处理 上一节我们介绍了缓存来减轻服务器的负担,这里的有条件的视图处理也从一定程度上减轻了服务器的负担,在正式介绍之前,先来看两个概念:Last-Modified和ETag Last-Mod ...

  9. window.screen.height和window.screen.availHeight和document.body.clientHeight和document.documentElement.clientHeight

    说这几个属性前 我说一下我的设备 我的设备有两个,一个高度为1080的显示器,一个高度为800的电脑 第一种:window.screen.height 这个方法是获取用户电脑屏幕的高度,是不关浏览器或 ...

  10. CocoaPods第三方类库依赖管理

    安装cocoapods   1.移除ruby的源地址 gem sources --remove https://rubygems.org/   2.添加ruby的源地址 gem sources -a ...