【AtCoder】CODE FESTIVAL 2016 qual C E-順列辞書 / Encyclopedia of Permutations
下文所有$s_i$均表示序列上第i位的数字
考虑对于一个位置的值$s_i$有什么东西能统计进去
考虑统计恰好在$i$这个这个位置分出大小的元素
发现对于一个$s_i$,答案是$\sum(s_i - 1 -\sum_{j=1}^{i-1}[s_j<s_i])*(N-i)!$
发现后面这个东西是统一乘的,于是只考虑前半部分
接下来对当前这个$s_i$的位置分类讨论
然后我们可以发现前半部分又可以拆成两个部分
$\sum s_i$ 和 $\sum_{j=1}^{i-1}[s_j<s_i]$
为了方便表达,接下来定义空的位置为$K$,未被填入的数字减一的和为$s$ 大于$s_i$的数的和为$Sum_i$
于是我们现在就要计算的是有多少合法的比它小的排列 和 其中$s_j<s_i$的个数
对于前面那个东西 分类
① 如果$s_i$不为空的话,那么$ans=(s_i - 1)*K!$
② 如果$s_i$为空的话 那么答案就是 $s * (K-1)!$[考虑每个数字填入 然后根据①中的式子合并一下==]
然后接下来就统计的是$s_j<s_i$的对数
还是分类 对于$s_j$ 和 $s_i$是否有取值分开讨论 2*2=4种
$(I)$
当$i$为空时
①$j$不为空
考虑空位中大于$j$位置上数的方案数,这个可以前缀统计一下,对于每个存在的$s_j$,$pre+=Sum_{s_j}*(K-1)!$[考虑空位中比它大的个数 其余随便填入]
②$j$为空
记前面的空位数量为$cnt$
等价于在$K$个数里选两个,剩下任意填入
即$cnt*C_{K}^{2}*(K-2)!$
$(II)$
当$i$不为空时
①如果$j$不为空
直接用树状数组维护小于$s_i$的值的个数==
②如果$j$为空
在没有填的数字里面选一个比$s_i$小的数填进去就完事了……
这个直接$cnt*(Sum_1-Sum_{s_i})*(K-1)!$
然后就做完了x
#include <bits/stdc++.h> using namespace std; const long long fish=1e9+7; const int mx=300000; bool pd[300005]; int p[300005],N,K; long long fac[300005],inv[300005],Sum[300005],ans[300005],Tree[300005]; long long Pow(long long x,int y){ long long ans=1; for (;y;y>>=1,x=1ll*x*x%fish) if (y&1) ans=1ll*ans*x%fish; return ans; } int Lowbit(int x){ return (x&(-x));} void Insert(int x,int y){for (int j=x;j<=mx;j+=Lowbit(j)) Tree[j]+=y;} long long query(int x) {long long ans=0;for (int i=x;i;i-=Lowbit(i)) ans+=Tree[i];return ans;} long long C(int n,int r){ if (n<r) return 0; return 1ll*fac[n]*inv[r]%fish*1ll*inv[n-r]%fish; } void Pre(){ scanf("%d",&N); fac[0]=1; for (int i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%fish; inv[N]=Pow(fac[N],fish-2); for (int i=N-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%fish; for (int i=1;i<=N;i++){ scanf("%d",&p[i]); if (p[i]) pd[p[i]]=true; else ++K; } } void Work(){ long long ss=0; for (int i=N;i>=1;i--){ Sum[i]+=Sum[i+1]; if (!pd[i]){ Sum[i]++; (ss+=(i-1))%=fish; } } long long Pre=0,Rem=0; for (int i=1;i<=N;i++){ if (!p[i]) { ans[i]=(ans[i]-Pre+fish)%fish; (ans[i]+=1ll*ss*fac[K-1]%fish)%=fish; if (K>=2) ((ans[i]-=1ll*Rem*C(K,2)%fish*fac[K-2]%fish)+=fish)%=fish; //cout<<C(K,2)<<endl; Rem++; } else{ (ans[i]+=1ll*(p[i]-1)*fac[K])%=fish; ans[i]=((ans[i]-(1ll*query(p[i]-1)*1ll*fac[K])%fish)+fish)%fish; if (K) ((ans[i]-=Rem*(Sum[1]-Sum[p[i]])%fish*fac[K-1]%fish)+=fish)%=fish; if (K) (Pre+=1ll*Sum[p[i]+1]*fac[K-1]%fish)%=fish; Insert(p[i],1); } } long long Ans=fac[K]; for (int i=1;i<=N;i++){ //cout<<ans[i]<<" "<<i<<endl; Ans=(Ans+1ll*fac[N-i]*ans[i]%fish)%fish; //cout<<i<<" "<<ans[i]<<endl; } cout<<(Ans+fish)%fish; return; } int main(){ Pre(); Work(); return 0; }