【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;
}