前言

感觉是最近做过最有意思的一道容斥计数啊,来补篇笔记。

思路

下文将满足 1a<b<c<dn1\leq a<b<c<d\leq n,且 pa,pb,pc,pdp_a,p_b,p_c,p_d 从小到大的排名在这四个数中依次为 1i,j,k,l41\leq i,j,k,l\leq 4 的四元组 (a,b,c,d)(a,b,c,d) 简称为 ijkl\text{ijkl},若排名不确定则用 x\text x 代替。例:1423\text{1423}1x2x\text{1x2x}

简单概括一下题意:求 11nn 的排列中 1324\text{1324} 的数量减去 1243\text{1243}1432\text{1432} 数量之和的结果。

看见这几个奇形怪状的东西第一反应是拿什么数据结构科技直接跑去暴力求(毕竟 1234 4321\text{1234}\ \text{4321} 这种东西很方便就能求出来,这几个也应该有办法对吧),但并没有什么科技能方便我们求出来。于是只能考虑容斥。

我们把要求的东西列一个式子,然后看看能不能转换成更好求的形式:

132412431432\text{1324}-\text{1243}-\text{1432}

=(1x2x1423)(12xx1234)(14xx1423)=(\text{1x2x}-\text{1423})-(\text{12xx}-\text{1234})-(\text{14xx}-\text{1423})

=1x2x12xx+123414xx=\text{1x2x}-\text{12xx}+\text{1234}-\text{14xx}

=1x2x1xxx+13xx+1234=\text{1x2x}-\text{1xxx}+\text{13xx}+\text{1234}

转换成这样之后,我们发现不仅式子里多出来了 1xxx\text{1xxx}1234\text{1234} 两个随便秒的东西,剩下两项的限制也比较松,瞬间就可做了许多。

于是对于每个 ii,我们先用树状数组预处理出 j<i,pj<pij<i,p_j<p_i 的个数 LiL_ij>i,pj>pij>i,p_j>p_i 的个数 RiR_i,左边小于自己的 pjp_j 的下标和(即 j×[pj<pi](j<i)\sum j\times [p_j<p_i](j<i)lsumilsum_i,以及左边小于自己的 pjp_j 的和(即 pj×[pj<pi](j<i)\sum p_j\times [p_j<p_i](j<i)lsumpilsump_i,这些东西都是计划的一部分。

1xxx、1234

比较容易,对于 1xxx\text{1xxx},枚举 11 的位置 ii,则答案显然为 C3Ri=n×(n1)×(n2)6C^{R_i}_3=\frac{n\times (n-1)\times (n-2)}{6};对于 1234\text{1234},设 fi,jf_{i,j} 为以 ii 结尾、长度为 jj 的上升子序列个数,则枚举 44 的位置 iifi,4f_{i,4} 即为答案,同样用树状数组扫一下即可。

1x2x

首先枚举 22,钦定 22 的位置为 ii,则左边正好有 LiL_i 个备选的 11

第二个 x\text x 的方案数显然为 nin-i,设第一个 x\text x 的位置为 jj,如果取答案为 Li×(i1)×(ni)L_i\times (i-1)\times (n-i),则有两种情况会被重复计算:jj11 左边或等于 iipj<pip_j<p_i。可以容斥,设 11 的位置为 kk,发现如果对每个 11 在答案里减掉 kk 就可以剔除所有的第一种情况和第二种情况中第一个 x>pi\text x>p_i 的情况。于是我们只要再减一个 1、x1、\text x 都来自 LiL_i 的情况,即 C2Li=Li×(Li1)2C^{L_i}_2=\frac{L_i\times (L_i-1)}{2}

于是这种情况的总数就是 [Li×(i1)lsumiLi×(Li1)2]×(ni)[L_i\times (i-1)-lsum_i-\frac{L_i\times (L_i-1)}{2}]\times (n-i)

13xx

这次我们枚举 33,则左边有 LiL_i 个备选的 11,同时我们在两个 x\text x 中钦定一个有 RiR_i 种备选的 44,则剩下一个有 pi1Lip_i-1-L_i 种备选的 22

直接取答案为 Li×Ri×(pi1Li)L_i\times R_i\times(p_i-1-L_i),会重复算上选出的 2211 小的情况。

我们可以搞一个有些人类智慧的容斥,暂时不管 2233 右边的限制,这样就是 Li×Ri×(pi1)L_i\times R_i\times (p_i-1),再对每个 j<i,pj<pij<i,p_j<p_i 减去一个 pjp_j,即对总答案减少上面算过的 lsumpilsump_i。设 11 位置为 jj22 位置为 kk,我们就可以直接排除掉 k<i,pkpjk<i,p_k\leq p_jk>i,pk<pjk>i,p_k<p_j,聪明的你一定发现了第二种其实就是 2<12<1,再减去第一种没有包括的 k<i,pk>pjk<i,p_k>p_j,即所有 C2Li=Li×(Li1)2C^{L_i}_{2}=\frac{L_i\times (L_i-1)}{2}pj<pk<pip_j<p_k<p_i 的有序对 (j,k)(j,k)

最后的总数是 Li×Ri×(pi1)lsumpiLi×(Li1)2L_i\times R_i\times (p_i-1)-lsump_i-\frac{L_i\times (L_i-1)}{2}

至此,我们已经算出来了需要的所有四元组的个数,这道题就做完了。

代码

发现题目的模数是 22 的整数次方,于是直接 unsigned int 自然溢出后再模就可以,省了不少麻烦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
constexpr int N = 2e5+5;

int n,p[N];
uint Is[N][5],lsum[N],lsump[N],L[N],R[N];

struct BIT{
#define lb(x) ((x)&(-(x)))
uint tr[N];
int mk[N],now = 0;
inline void clr(){now++;}
inline void add(int x,uint v){
while(x<=n){
if(mk[x]^now) mk[x] = now,tr[x] = 0;
tr[x]+=v,x+=lb(x);
}
}
inline uint query(int x){
uint res = 0;
while(x){
if(!(mk[x]^now)) res+=tr[x];
x-=lb(x);
}
return res;
}
inline uint query(int l,int r){return query(r)-query(l-1);}
}bit;

void solve(){
read(n);
for(int i=1;i<=n;i++) read(p[i]);
for(int i=1;i<=n;i++){
L[i] = Is[i][2] = bit.query(1,p[i]-1);
bit.add(p[i],1);
}
bit.clr();
for(int i=n;i>=1;i--){
R[i] = bit.query(p[i]+1,n);
bit.add(p[i],1);
}
bit.clr();
for(int j=3;j<=4;j++){
for(int i=1;i<=n;i++){
Is[i][j] = bit.query(1,p[i]-1);
bit.add(p[i],Is[i][j-1]);
}
bit.clr();
}
for(int i=1;i<=n;i++){
lsum[i] = bit.query(1,p[i]-1);
bit.add(p[i],i);
}
bit.clr();
for(int i=1;i<=n;i++){
lsump[i] = bit.query(1,p[i]-1);
bit.add(p[i],p[i]);
}
uint ans = 0;
for(int i=1;i<=n;i++){
ans = ans+(L[i]*(i-1)-lsum[i]-((ull)L[i]*(L[i]-1)/2))*R[i];
ans = ans-((ull)R[i]*(R[i]-1)*(R[i]-2)/6);
ans = ans+Is[i][4];
ans = ans+(R[i]*(L[i]*(p[i]-1)-lsump[i]-(ull)L[i]*(L[i]-1)/2));
}
cout<<ans%16777216;
}