【XSY2714】大佬的难题 数学 树状数组

题目描述

  给你三个排列\(A,B,C\),求

\[\sum_{1\leq x,y\leq n}[a_x<a_y][b_x<b_y][c_x<c_y] \]

  \(n\leq 2\times {10}^6\)

题解

  就是一个三位偏序。用CDQ分治可以做到\(O(n\log^2 n)\)常熟小一点可以卡过。我在UOJ上面能跑过去。

  这道题只有一个特殊性质:\(A,B,C\)都是排列。

  我们记

\[\begin{align} K_{x,y}&=[a_x<a_y]+[b_x<b_y]+[c_x<c_y]\\ S_{x,y}&=\max(K_{x,y},K_{y,x})\\ A&=\sum_{1\leq x\leq y\leq n}[S_{x,y}=3]\\ B&=\sum_{1\leq x\leq y\leq n}[S_{x,y}=2]\\ P_{a,b}&=\sum_{1\leq x,y\leq n}[a_x<a_y][b_x<b_y]\\ P_{a,c}&=\sum_{1\leq x,y\leq n}[a_x<a_y][c_x<c_y]\\ P_{b,c}&=\sum_{1\leq x,y\leq n}[b_x<b_y][c_x<c_y]\\ \end{align} \]

  那么我们要求的答案就是\(A\)了。

  容易发现

\[\begin{align} A+B&=\binom{n}{2}\\ 3A+B&=P_{a,b}+P_{a,c}+P_{b,c} \end{align} \]

  原问题转化为二位偏序。

  用树状数组即可解决。

  时间复杂度:\(O(n\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
ll seed;
ll rd()
{
	return seed=((seed*19260817)^233333)&((1<<24)-1);
}
int n;
void gen(int *a)
{
	int i;
	for(i=1;i<=n;i++)
		a[i]=i;
	for(i=1;i<=n;i++)
		swap(a[i],a[rd()%i+1]);
}
int a1[2000010];
int a2[2000010];
int a3[2000010];
int a[2000010];
int c[2000010];
void add(int x)
{
	for(;x<=n;x+=x&-x)
		c[x]++;
}
int sum(int x)
{
	int s=0;
	for(;x;x-=x&-x)
		s+=c[x];
	return s;
}
ll gao(int *a1,int *a2)
{
	memset(c,0,sizeof c);
	int i;
	for(i=1;i<=n;i++)
		a[a1[i]]=a2[i];
	ll s=0;
	for(i=1;i<=n;i++)
	{
		s+=sum(a[i]);
		add(a[i]);
	}
	return s;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%d",&n);
	scanf("%lld",&seed);
	gen(a1);
	scanf("%lld",&seed);
	gen(a2);
	scanf("%lld",&seed);
	gen(a3);
	ll ans=0;
	ans+=gao(a1,a2);
	ans+=gao(a1,a3);
	ans+=gao(a2,a3);
	ans-=ll(n-1)*n/2;
	ans/=2;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-03-06 11:45  ywwyww  阅读(177)  评论(0编辑  收藏  举报