hdu 6059 Kanade's trio(字典树+位压缩)

Kanade’s trio
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 890 Accepted Submission(s): 322

Problem Description
Give you an array A[1..n]
,you need to calculate how many tuples (i,j,k)satisfy that (i《j《k)
and ((A[i] xor A[j])<(A[j] xor A[k]))

There are T test cases.

1≤T≤20

1≤∑n≤5∗10^5

0≤A[i]<2^30

Input
There is only one integer T on first line.

For each test case , the first line consists of one integer n
,and the second line consists of nintegers which means the array A[1..n]

Output
For each test case , output an integer , which means the answer.

Sample Input
1

5

1 2 3 4 5

Sample Output
6

题意:
求有多少组元组,使(i《j《k)
and ((A[i] xor A[j])<(A[j] xor A[k]))
条件成立

解析:
用字典树构造,将一个数用二进制表示,每一个节点有两个孩子节点(0和1)
根据再a数组中的位置从小到大进行插入
每一次插入,同时计算在插入的这一点作为Ak时对答案的贡献,分为两种情况(根据异或运算符的性质,就是找Ak与Ai最高位的不同的值,然后找在该位与Ai相同的Aj):

num[i][0/1]表示第i位时0/1的数的个数
trie[i].igj 表示在这个数被选为ai时,有多少编号小于i且前i位与Ai不同的数
(trie[p].igj+=num[i][d]-trie[p].cnt,表示在第i位为d的数的数量-第i位为d且前i位与Ai相同的个数)

(1)Ai与Ak前t-1位相同,第t位不同,Aj与Ai前t-1位相同,且第t位相同(就是在n个里面选2个,C(2,n))
式子:ans+=(ll)trie[tmp].cnt*(trie[tmp].cnt-1)/2;
(2)Ai与Ak前t-1位相同,第t位不同,Aj与Ai前t-1位不同,且第t位相同
(3)在第二步的计算时候,会把(i>j)的情况算进去,所以就要把这一部分减去(trie[i].igj表示)
式子:ans+=(ll)trie[tmp].cnt(//Ai)*(num[i][d^1]-trie[tmp].cnt)(//Aj)-trie[tmp].igj(i>j的情况);

#include<stdio.h>
#include<string.h>
typedef long long ll;

const int BIT = 30;
const int MAXN = 500010;

typedef struct node
{
    int cnt;
    int igj;   //每一个ai中i>j的aj的个数和
    int nxt[2];
}node;

node trie[MAXN*BIT];
int sz;
int num[BIT][2];
int digt[BIT];
ll ans;



void insert()
{
    int p=0;
    for(int i=0;i<BIT;i++)
    {
        int d=digt[i];  //p表示d的父节点
        //num[i][d]++;
        if(!trie[p].nxt[d])  //若该节点i还没有点到达过
            trie[p].nxt[d]=++sz;
        if(trie[p].nxt[d^1])  //若该节点i的兄弟节点到达过,或者说有值
        {
            int tmp=trie[p].nxt[d^1];
            ans+=(ll)trie[tmp].cnt*(trie[tmp].cnt-1)/2;
            ans+=(ll)trie[tmp].cnt*(num[i][d^1]-trie[tmp].cnt)-trie[tmp].igj;   //ai*aj-(i>j)
        }

        p=trie[p].nxt[d];
        trie[p].cnt++;
        trie[p].igj+=num[i][d]-trie[p].cnt;    //num[i][d]表示所有a的第i位(从左到右)是d的数-ai
    }
}



int main()
{
    int t,n,a;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(num,0,sizeof(num));
        memset(trie,0,sizeof(trie));
        ans=sz=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            int tmp=BIT-1;
            while(tmp!=-1)
            {
                num[tmp][a&1]++;
                digt[tmp--]=a&1;
                a=a>>1;
            }
            insert();
        }
        printf("%lld\n",ans);

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值