BZOJ2837 : 小强的形状

离散化后通过树状数组求出:

b[i]为i之前比它小的。

c[i]为i之前比它大的=i-1-i之前小于等于它的。

d[i]为i之后比它小的。

e[i]为i之后比它大的=n-i-f[i]。

f[i]为i之后小于等于它的。


则:

$cnt_{123}=\sum_{i=1}^n b[i]e[i]$

$cnt_{321}=\sum_{i=1}^n c[i]d[i]$

$cnt_{213}$=将序列a翻转后的$cnt_{312}$

$cnt_{231}$=将序列a翻转后的$cnt_{132}$

$cnt_{312}=\sum_{i=1}^n b[i]c[i]-cnt_{132}$

$cnt_{132}$可以这样计算:

枚举$3$的位置$i$,它右边比它小的$k$有$b[k]$的贡献,还要减去$i$右下角内$12$形状的二元组的个数,即右下角$b[k]+f[k]$的和$-C_{d[i]}^2$。

 

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

 

#include<cstdio>
#include<algorithm>
#define N 100010
typedef long long ll;
int n,i,a[N],b[N],c[N],d[N],e[N],f[N];ll bit[N],c1,c2,c3,c4,c5,c6,all;
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline int lower(int x){
  int l=1,r=n,mid,t;
  while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1;
  return t;
}
inline void add(int x,int y){for(;x<=n;x+=x&-x)bit[x]+=y;}
inline ll ask(int x){ll t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
void write(ll x){for(printf("0."),i=0;i<20;i++)x*=10,printf("%d",x/all),x%=all;puts("");}
int main(){
  for(read(n),i=1;i<=n;i++)read(a[i]),b[i]=a[i];
  for(std::sort(b+1,b+n+1),i=1;i<=n;i++)a[i]=lower(a[i]);
  for(i=1;i<=n;i++)b[i]=ask(a[i]-1),c[i]=i-1-ask(a[i]),add(a[i],1);
  for(i=1;i<=n;i++)bit[i]=0;
  for(i=n;i;i--)d[i]=ask(a[i]-1),f[i]=ask(a[i]),e[i]=n-i-f[i],add(a[i],1);
  for(i=1;i<=n;i++)bit[i]=0;
  for(i=n;i;i--){
    c1+=1LL*b[i]*e[i],c6+=1LL*c[i]*d[i];
    c5+=1LL*b[i]*c[i],c2+=ask(a[i]-1)-1LL*d[i]*(d[i]-1)/2,add(a[i],b[i]+f[i]);
  }
  c5-=c2;
  for(i=1;i<n-i+1;i++)std::swap(a[i],a[n-i+1]);
  for(i=1;i<=n;i++)bit[i]=0;
  for(i=1;i<=n;i++)b[i]=ask(a[i]-1),c[i]=i-1-ask(a[i]),add(a[i],1);
  for(i=1;i<=n;i++)bit[i]=0;
  for(i=n;i;i--)d[i]=ask(a[i]-1),f[i]=ask(a[i]),add(a[i],1);
  for(i=1;i<=n;i++)bit[i]=0;
  for(i=n;i;i--)c3+=1LL*b[i]*c[i],c4+=ask(a[i]-1)-1LL*d[i]*(d[i]-1)/2,add(a[i],b[i]+f[i]);
  c3-=c4;
  all=c1+c2+c3+c4+c5+c6;
  write(c1),write(c2),write(c3),write(c4),write(c5),write(c6);
  return 0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值