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("
");
	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;
}
原文地址:https://www.cnblogs.com/lcezych/p/13827609.html