$n \leq 100000$的三个排列,求三元组$(x,y,z)$数量。一个合法的$(x,y,z)$是指存在一个下标集合$S$,$(x,y,z)=(max_{i \in S}a_i,max_{j \in S}b_j,max_{k \in S}c_k)$。
是一个数点问题。观察到:$S$中有用的下标只有1~3个,可分类。
有用下标有一个:$n$个下标均可。
有用下标有两个:可以用总的二元组$\frac{n(n-1)}{2}$减去满足$a_i>a_j,b_i>b_j,c_i>c_j$的数量。三维数点。
有用下标有三个:还是容斥,把三元组中一个下标有用、两个下标有用的情况排除掉。
一个下标有用的三元组:设一个$i$有$x$个$j$满足$a_i>a_j,b_i>b_j,c_i>c_j$,那么他在这的贡献是$\frac{x(x-1)}{2}$。
两个下标有用的三元组:这样的三元组,会有一个下标在两个数组中最大,一个下标在第三个数组最大,另一个没用。因此枚举两个数组,统计有多少是“一个下标在两个数组中最大的三元组”,这样的话上面那个“一个下标有用的三元组”会算3次,两个下标有用的三元组会算1次,减一下即可。
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 #include<stdlib.h> 5 //#include<queue> 6 #include<iostream> 7 using namespace std; 8 9 int n; 10 #define maxn 200011 11 #define LL long long 12 struct Poi{int a,b,c,ans;}p[maxn]; 13 bool cmpa(const Poi &a,const Poi &b) {return a.a<b.a;} 14 bool cmpb(const Poi &a,const Poi &b) {return a.b<b.b;} 15 16 struct BIT 17 { 18 int a[maxn],n; 19 void clear(int m) {n=m; memset(a,0,sizeof(a));} 20 void add(int x,int v) {for (;x<=n;x+=x&-x) a[x]+=v;} 21 int query(int x) {int ans=0; for (;x;x-=x&-x) ans+=a[x]; return ans;} 22 }t; 23 24 void solve(int L,int R) 25 { 26 if (L==R) return; 27 int mid=(L+R)>>1; 28 solve(L,mid); solve(mid+1,R); 29 int i=L,j=mid+1; 30 while (i<=mid && j<=R) 31 { 32 if (p[i].b<p[j].b) t.add(p[i].c,1),i++; 33 else p[j].ans+=t.query(p[j].c-1),j++; 34 } 35 while (j<=R) p[j].ans+=t.query(p[j].c-1),j++; 36 for (i--;i>=L;i--) t.add(p[i].c,-1); 37 sort(p+L,p+R+1,cmpb); 38 } 39 40 void c3() 41 { 42 for (int i=1;i<=n;i++) p[i].ans=0; 43 sort(p+1,p+1+n,cmpa); 44 t.clear(n); 45 solve(1,n); 46 } 47 48 void c2(int type) 49 { 50 for (int i=1;i<=n;i++) p[i].ans=0; t.clear(n); 51 if (type==1) sort(p+1,p+1+n,cmpb); else sort(p+1,p+1+n,cmpa); 52 if (type==0) for (int i=1;i<=n;i++) p[i].ans=t.query(p[i].b-1),t.add(p[i].b,1); 53 else for (int i=1;i<=n;i++) p[i].ans=t.query(p[i].c-1),t.add(p[i].c,1); 54 } 55 56 int main() 57 { 58 scanf("%d",&n); 59 for (int i=1;i<=n;i++) scanf("%d",&p[i].a); 60 for (int i=1;i<=n;i++) scanf("%d",&p[i].b); 61 for (int i=1;i<=n;i++) scanf("%d",&p[i].c); 62 63 c3(); 64 LL ans=n+1ll*n*(n-1)/2+1ll*n*(n-1)*(n-2)/6; 65 for (int i=1;i<=n;i++) ans-=p[i].ans-1ll*p[i].ans*(p[i].ans-1); 66 c2(0); for (int i=1;i<=n;i++) ans-=1ll*p[i].ans*(p[i].ans-1)/2; 67 c2(1); for (int i=1;i<=n;i++) ans-=1ll*p[i].ans*(p[i].ans-1)/2; 68 c2(2); for (int i=1;i<=n;i++) ans-=1ll*p[i].ans*(p[i].ans-1)/2; 69 printf("%lld\n",ans); 70 return 0; 71 }