qbxt DAY3 T4
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("\n");
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\n",ans);
return 0;
}

浙公网安备 33010602011771号