【容斥原理】【推导】【树状数组】Gym - 101485G - Guessing Camels
题意:给你三个1~n的排列a,b,c,问你在 (i,j)(1<=i<=n,1<=j<=n,i≠j),有多少个有序实数对(i,j)满足在三个排列中,i都在j的前面。
暴力求的话是三维偏序,相对比较困难。但是我们可以用一些简单的方法。
设在a中i在j前面的有序实数对数为A,b中为B,c中为C。(其实显然A=B=C=n*(n-1)/2)
要求的即为A∩B∩C。
利用容斥原理A+B+C-A∩B-A∩C-B∩C+A∩B∩C=A∪B∪C ①,以及Ω-A∪B∪C=A∩B∩C ②可容易求得。(显然Ω=n*(n-1))
②式怎么来的呢?显然,在三个序列中均满足i在j前面的有序实数对(i,j),必然与在三个序列中都未出现的有序实数对(j,i)一一对应,证毕。
而A∩B、A∩C、B∩C都可以通过二维偏序(排序+树状数组)求得。
所以最后答案就是(n*(n-1)-(n*(n-1)/2*3-A∩B-A∩C-B∩C))/2。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int n; int d[200005]; void Update(int p){for(;p<=n;p+=(p&(-p))) ++d[p];} int Query(int p){int res=0; for(;p;p-=(p&(-p))) res+=d[p]; return res;} ll A[3]; struct data{ int x,y; data(const int &x,const int &y){ this->x=x; this->y=y; } data(){} }a[200005],b[200005],c[200005],p[3][200005]; bool operator < (const data &a,const data &b){ return a.x<b.x; } int main(){ // freopen("g.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i].x); a[i].y=i; } sort(a+1,a+n+1); for(int i=1;i<=n;++i){ scanf("%d",&b[i].x); b[i].y=i; } sort(b+1,b+n+1); for(int i=1;i<=n;++i){ scanf("%d",&c[i].x); c[i].y=i; } sort(c+1,c+n+1); for(int i=1;i<=n;++i){ p[0][i]=data(a[i].y,b[i].y); p[1][i]=data(a[i].y,c[i].y); p[2][i]=data(b[i].y,c[i].y); } for(int i=0;i<3;++i){ sort(p[i]+1,p[i]+n+1); memset(d,0,sizeof(d)); for(int j=1;j<=n;++j){ A[i]+=(ll)Query(p[i][j].y); Update(p[i][j].y); } } printf("%I64d\n",((ll)n*(ll)(n-1)-((ll)n*(ll)(n-1)/2ll*3ll-A[0]-A[1]-A[2]))/2ll); return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/