DAY3 T4
显然枚举区间不现实,考虑常用套路:计算每一个值的贡献,即被多少个区间经过
当确定中间点(j)后,对于(i<j,k>j, a_i >a_j, a_k > a_j),发现包含(a_i)的区间左端点有(i)个,包含(a_k)的区间右端点有(n-k+1)个,则包含((i,j,k))这个三元组的区间有(i*(n-k+1))个
建立一个值域树状数组,先对数组从前向后遍历,设(pre[i])表示(i)之前有多少区间经过。每次将树状数组下标为(a_i)的点加上(i)用来计算下一次询问的答案。然后从后向前遍历,做法和前面的相同。
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
#define lb(x) ((x)&(-(x)))
const int maxn=100010;
int n,z[maxn];
long long y[maxn],pre[maxn],suf[maxn];
long long query(int p)
{
long long ans=0;
for (;p;p-=lb(p))
ans+=y[p];
return ans;
}
void modify(int p,int v)
{
for (;p<=n;p+=lb(p))
y[p]+=v;
}
int main()
{
scanf("%d",&n);
for (int a=1;a<=n;a++)
scanf("%d",&z[a]);
memset(y,0,sizeof(y));
for (int a=1;a<=n;a++)
{
pre[a]=query(n)-query(z[a]);
modify(z[a],a);
}
for(int i = 1; i <= n; i ++) printf("%d ", pre[i]);
printf("
");
memset(y,0,sizeof(y));
for (int a=n;a>=1;a--)
{
suf[a]=query(n)-query(z[a]);
modify(z[a],n-a+1);
}
for(int i = 1; i <= n; i ++) printf("%d ", suf[i]);
long long ans=0;
for (int a=1;a<=n;a++)
ans+=pre[a]*suf[a];
//printf("%lld
",ans);
return 0;
}