HDU-6059 Kanade's trio(字典树)

来源:互联网 发布:传统企业转型网络案例 编辑:程序博客网 时间:2024/06/05 06:17

传送门:HDU-6059

题目要求计算Ai^Aj < Aj^Ak的种数

题解:字典树

对于这道题首先考虑到,假如Ai和Ak前t-1位都相同,第t位不相同时,如果Ai的第t位等于Aj的第t位,Ak的第t位不等于Aj的第t位,那么Ai^Aj一定小于Aj^Ak的,那么只要在字典树中从高位往低位依次考虑即可。

v表示这个节点添加的次数,sum表示在这个数的前面有多少个数的前t-1位与这个数的前t-1位是不同的(第t位相同),cnt[i][0/1]表示有多少个数字的第i位是0/1

枚举Ak的每一位,对于Ak的第t位记作tmp

现在只讨论如果tmp==1时对答案的贡献(tmp==0时的求法相同)

L[now].nxt[tmp^1].v*(L[now].nxt[tmp^1].v-1)/2+(cnt[t][tmp^1]-L[now].nxt[tmp^1].v)*L[now].nxt[tmp^1].v-L[now].sum[tmp^1]

1)前一部分表示Ai,Aj和Ak的前t-1位都相同时对答案的贡献

2)中间一部分是Ai的前t-1位与Ak的前t-1相同,Aj的前t-1位不与Ak的前t-1相同时对答案的贡献(乍看一下第二部分没有问题,其实里面包含了非法部分,即有些前t-1位与Ak不相同的数被记作了Ai,因此需要第三部分

3)后面一部分表示Ai的前t-1位与Ak的前t-1位不同时对答案的贡献(为负数,因为计算时规定了Ai的前t-1位要与Ak的前t-1位相同)



#include<stdio.h>#include<iostream>#include<algorithm>#include<stdlib.h>#include<math.h>#include<string.h>#include<set>#include<vector>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define first x#define second y#define eps 1e-5using namespace std;typedef long long LL;typedef pair<int, int> PII;const int inf = 0x3f3f3f3f;const int MX = 5e7 + 5;struct node {    int nxt[2];    LL sum[2];    int v;    void init() {        sum[0] = sum[1] = 0;        nxt[0] = nxt[1] = -1;        v = 0;    }} L[MX];int tot;LL ans, cnt[32][2];void add(int a[], int len) {    int now = 0, tmp;    for (int i = len - 1; i >= 0; i--) {        tmp = a[i];        if (L[now].nxt[tmp] == -1) {            L[++tot].init();            L[now].nxt[tmp] = tot;        }        if (L[now].nxt[tmp ^ 1] != -1)  {            ans += L[L[now].nxt[tmp ^ 1]].v * (L[L[now].nxt[tmp ^ 1]].v - 1) / 2;            ans += L[L[now].nxt[tmp ^ 1]].v * (cnt[i][tmp ^ 1] - L[L[now].nxt[tmp ^ 1]].v);            ans -= L[now].sum[tmp ^ 1];        }        L[now].sum[tmp] += cnt[i][tmp] - L[L[now].nxt[tmp]].v;        cnt[i][tmp] ++;        now = L[now].nxt[tmp];        L[now].v++;    }}int a[500005], b[35];int main() {    //freopen("in.txt", "r", stdin);    int T, n;    scanf("%d", &T);    while (T--) {        scanf("%d", &n);        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);        memset(cnt, 0, sizeof(cnt));        tot = ans = 0;        L[0].init();        for (int i = 1; i <= n; i++) {            for (int j = 0; j < 30; j++) b[j] = a[i] >> j & 1;            add(b, 30);        }        printf("%lld\n", ans);    }    return 0;}


原创粉丝点击