HDU 6059 Kanade's trio(2017 Multi-University Training Contest 3)

来源:互联网 发布:烈焰传奇翅膀进阶数据 编辑:程序博客网 时间:2024/05/29 14:27

题目链接:Kanade’s trio
题意:给出一个序列A,问存在多少个不同的三元组满足Ai xor Aj<Aj xor Ak (i<j<k)
题解:对于每一个数,我们把它转化成30位的二进制形式,并按照序列顺序依次插入到0/1字典树中,插入过程中,我们把当前插入的数作为Ak,每次计算其贡献加起来即为答案。
我们考虑三个数a,b,c。易得只有当ac的不同二进制位的最高位ai,ci满足ai xor bi<bi xor ciai xor bi=0 and bi xor ci=1,可得ai=bi and bici。所以我们只要在插入过程中记录对应二进制位满足条件的对数即可。
那么贡献共有3种,假设已经插入到了Ak的第t位:
(1).Ai[1...t1]=Aj[1...t1]那么记节点now代表c的子节点为son[now][c],节点now被经过次数为cnt[now],设tmp=cnt[son[now][1Ak[t]]]则贡献是tmp(tmp1)/2
(2).Ai[1...t1]Aj[1...t1],记所有数中第t位为c的个数为sum[t][c],那这一部分贡献为(sum[now][1Ak[t]]tmp)tmp
(3).显然,在(2)中,存在不合法情况Ai[1...t1]Aj[1...t1] and Aj[1...t1]=Ak[1...t1]。所以我们要减去这一部分。而对于每一个节点now,其不合法贡献为sum[now][Ak[t]]cnt[son[now][Ak[t]]]
综上,我们把每一个节点前两种贡献加起来然后减去第三种贡献即为最终答案。

#include <bits/stdc++.h>using namespace std;const int N = 500005*30;int a[N][30],sum[30][2],cnt[N],bs[N],n,id,k,st,t,d,dd;;int main(){    int T;    scanf("%d",&T);    while(T--){        for(int i=0;i<=id;i++){            for(int j=0;j<30;j++)                a[i][j]=0;            cnt[i]=bs[i]=0;        }        for(int i=0;i<30;i++)            sum[i][0]=sum[i][1]=0;        id=0;        scanf("%d",&n);        long long ans=0;        for(int i=1;i<=n;i++){            scanf("%d",&k);            st=0;            for(int i=29;i>=0;i--){                t=(k>>i)&1;                if(!a[st][t])                    a[st][t]=++id;                d=a[st][t];                dd=a[st][!t];                if(dd)                    ans+=1LL*cnt[dd]*(cnt[dd]-1)/2+                    1LL*cnt[dd]*(sum[i][!t]-cnt[dd])-bs[dd];                bs[d]+=sum[i][t]-cnt[d];                cnt[d]++;                sum[i][t]++;                st=d;            }        }        printf("%lld\n",ans);    }    return 0;}

参考博客:
太阳星人FxxL

原创粉丝点击