hdu 6059 | 2017 Multi-University Training Contest - Team 3 Kanade's trio (trie)

http://acm.hdu.edu.cn/showproblem.php?pid=6059

题意:

含 N 个数字的 A 数组,求有多少个三元组 (i,j,k) 满足 i<j<k 且a[i]^a[j] < a[j]^a[k]

思路:

考虑a[i]和a[k]二进制不同位中的最高位,此时满足题意的a[j]是该位与a[i]相同,其他位任意的所有a[j]的个数。

我们可以从1..n,依次插入a[k]到trie中,插入时,顺便用num[i][j]统计二进制第i位为j的数的个数。

当要插入a[k]时,a[1]..a[k-1]已经插入到了trie中。

trie上统计当某个节点,该位为0的数的个数和该为为1的数的个数。

需要注意这样统计出的数并不能保证i<j (但是可以保证i<k。。。)

因为我们的trie需要额外维护一部分ext.具体解释见代码注释。

  1/* ***********************************************
  2Author :111qqz
  3Created Time :2017年11月12日 星期日 01时30分29秒
  4File Name :6059.cpp
  5************************************************ */
  6
  7#include <bits/stdc++.h>
  8#define PB push_back
  9#define fst first
 10#define sec second
 11#define lson l,m,rt<<1
 12#define rson m+1,r,rt<<1|1
 13#define ms(a,x) memset(a,x,sizeof(a))
 14typedef long long LL;
 15#define pi pair < int ,int >
 16#define MP make_pair
 17
 18using namespace std;
 19const double eps = 1E-8;
 20const int dx4[4]={1,0,0,-1};
 21const int dy4[4]={0,-1,1,0};
 22const int inf = 0x3f3f3f3f;
 23const int N=5E5+7;
 24int a[N];
 25int num[35][2];//cnt[i][j] 表示二进制表示中第i位为j的数的个数
 26struct Trie
 27{
 28    struct Node
 29    {
 30        Node *nxt[2];
 31        LL cnt[2];//需要统计前缀为某个串,该位置为0和该位置为1的个数。
 32    LL ext[2];
 33        Node()
 34        {
 35            for ( int i = 0 ; i < 2;  i++) nxt[i] = NULL;
 36            cnt[0]=cnt[1]=0 ;
 37        ext[0]=ext[1]=0;
 38        }
 39    };
 40    Node *root;
 41    void init()
 42    {
 43        root = new Node();
 44    }
 45    Trie()
 46    {
 47        root = new Node();
 48    }
 49    void Insert(int x)
 50    {
 51        Node *u = root;
 52    for ( int i = 29 ; i >= 0 ; i--)
 53        {
 54            int y = (x>>i)&1;
 55        num[i][y]++; //统计二进制第i位为y的数的个数
 56        u->cnt[y]++; //统计trie树上当前节点数的个数
 57        u->ext[y]+=num[i][y]; //把此时插入的数看做a[i],那么u->ext[y]就是满足j<=i 的j的数目
 58    //因为之后要用到,所以要提前维护
 59            if (u->nxt[y]==NULL) u->nxt[y] = new Node();
 60            u = u->nxt[y];
 61        }
 62    }
 63    LL Cal( int x)
 64    {
 65    Node *u = root;
 66    LL res = 0 ;
 67    for ( int i = 29 ; i >= 0 ; i--)
 68    {
 69        int y =  (x>>i)&1;
 70        res += num[i][y^1]*u->cnt[y^1]-u->ext[y^1];
 71//对于此时插入的a[k]的二进制第i位(从低往高)的数y,只有当a[i]和a[j]的第i位为1-y时,才会贡献答案。
 72    // num[i][y^1]为第i位为1-y的a[j]的个数(a[j]的其他位,包括比i高的位和比i低的位都不受限制
 73    // u->cnt[y^1]表示trie树上,从rt到p节点所表示的二进制位上,a[i]与a[k]一直相同,p的下一个节点a[i]与a[k]的二进制位不同 的 a[i]的个数
 74        u = u->nxt[y];
 75        if (u==NULL) break;
 76    }
 77    return res;
 78    }
 79}trie;
 80void solve()
 81{
 82    trie.init();
 83    ms(num,0);
 84    LL ans = 0 ;
 85    int n;
 86    scanf("%d",&n);
 87    for ( int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
 88    for ( int i = 1 ; i <= n ; i++)
 89    {
 90    ans = ans + trie.Cal(a[i]);
 91    trie.Insert(a[i]);
 92    }
 93    cout<<ans<<endl;
 94}
 95
 96int main()
 97{
 98    #ifndef  ONLINE_JUDGE 
 99    freopen("./in.txt","r",stdin);
100  #endif
101    int T;
102    cin>>T;
103    while (T--) solve();
104
105  #ifndef ONLINE_JUDGE  
106  fclose(stdin);
107  #endif
108    return 0;
109}