cdq(2018.10.18)

一句话题意:给你三个数列{a_i},{b_i},{c_i},保证每个数列都恰好是一个排列。你需要求出满足\(a_i<a_j,b_i<b_j,c_i<c_j\)的有序对\((i,j)\)的数目。
提示:为了避免过量的输入对程序的运行效率产生影响,数列为生成的。
数据范围:
对于 100% 的数据,保证\(1≤n≤ 2*10^6\),
看似一个三维偏序,实际上cdq分治只能36分,还得卡常才能获得高于36分的好成绩。
事实上,因为这三个序列是排列,所以我们可以将其转化为3个二维偏序,如下:

\[x=\sum_{i,j}[a_i<a_j][b_i<b_j] \]

\[y=\sum_{i,j}[a_i<a_j][c_i<c_j] \]

\[z=\sum_{i,j}[b_i<b_j][c_i<c_j] \]

我们知道合法方案必定满足上面任意一个二维偏序,而不合法方案最多满足上面的一个二维偏序,因为如果满足两个,就能满足第三个。
所以可以设合法方案数为sum,不合法方案数为k
那么有:

\[x+y+z=3*sum+k \]

然后考虑怎么去掉不合法方案
我们知道每一个数对\((i,j)\)可能合法也可能不合法,所以所有的数对\((i,j)\)就包括\((sum+k)\),数对总数就是组合数啦\(C^2_n\)
代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2e6+5;
unsigned int SA,SB,SC;int n,d[N],b[N],c[N];
unsigned int rd(){
    SA^=SA<<16;SA^=SA>>5;SA^=SA<<1;
    unsigned int t=SA;SA=SB;SB=SC;SC^=t^SA;return SC;
}
void gen(int *P){
    for (int i=1;i<=n;++i) P[i]=i;
    for (int i=1;i<=n;++i) swap(P[i],P[1+rd()%n]);
}
#define mid ((l+r)>>1)
#define lowbit(i) (i&(-i))
int f[N];long long ans,x,y,z;
struct oo{int a,b,c;}a[N];
void add(int x){for(int i=x;i<=n;i+=lowbit(i))f[i]++;}
int get(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=f[i];return ans;}
bool cmp1(oo a,oo b){return a.a<b.a;}
bool cmp2(oo a,oo b){return a.b<b.b;}
int main()
{
    scanf("%d%u%u%u",&n,&SA,&SB,&SC);
    gen(d);gen(b);gen(c);
    for(int i=1;i<=n;i++)a[i]=(oo){d[i],b[i],c[i]};
    sort(a+1,a+n+1,cmp1);
    for(int i=1;i<=n;i++)x+=get(a[i].b),add(a[i].b);
    for(int i=1;i<=n;i++)f[i]=0;
    for(int i=1;i<=n;i++)y+=get(a[i].c),add(a[i].c);
    for(int i=1;i<=n;i++)f[i]=0;
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;i++)z+=get(a[i].c),add(a[i].c);
    ans=1ll*n*(n-1)/2;
    printf("%lld\n",(x+y+z-ans)>>1);
}
posted @ 2018-10-18 17:24  蒟蒻--lichenxi  阅读(373)  评论(0编辑  收藏  举报