2017多校第3场 HDU 6059 Trie树,贡献统计

来源:互联网 发布:本地搜索引擎seo 编辑:程序博客网 时间:2024/06/10 01:11

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6059

题意:如题。

解法:这道题,赛后一时没有懂,今天拿出来看了题解,瞬间懂了,开深。我把自己对这道题的理解写一下。


1,需要做什么?我们需要枚举数字的每一位,建立一个前缀字典树,sum【】记录这个节点添加的个数,x【】代表不符合要减去的个数,tot【i】【j】代表第i位的第j位的 数量。


2,怎么做?枚举a[k]的每一位,对于a[k]的第cur位Kcurp,如果Kcurp等于1的话,那么对答案产生贡献的Icurp和Jcurp(Icurp代表a[i]的第cur位,Jcurp代表a[j]的第cur位)一定为0,而且a[i]的前cur位和a[k]的前cur位必定相同,这个自己推一推就明白了,然后再记一下a[Kcurp]对应的节点为p,同父亲的另一个儿子对应pp。


那么如何计算答案?


3,如果从0-Kcurp的 每一位,a[i],a[j],a[k]都相等时,ak[Kcurp]加入后贡献是sum[p]*(sum[p]-1)/2.


4,ai【0】-ai【Kcurp-1】和aj【0】-aj【Kcurp-1】有部分不想等,ai【0】~ai【Kcurp-1】和ak【0】~ak【Kcurp-1】相等时,贡献是sum[p]*(tot[i][b[i]^1]-sum[p])-x[p]。(下面说x[p])


5,x【p】表示加入一个点,产生的不合法贡献,就是tot[i][b[i]]-sum[pp].


#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 500010;const int maxs = 35;int T,n,b[maxs];int cnt,sum[maxn*maxs],ch[maxn*maxs][2],tot[maxs][2];LL ans,x[maxn*maxs];void insert(){    int now = 0;    for(int i = 0; i < 31; i++){        int go = b[i];        if(!ch[now][go]){            ch[now][go] = ++cnt;            memset(ch[cnt], 0, sizeof(ch[cnt]));            x[cnt] = sum[cnt] = 0;        }        //cal answer        int p = ch[now][go^1], pp = ch[now][go];        ans += 1LL*sum[p]*(sum[p]-1)/2;        ans += 1LL*sum[p]*(tot[i][go^1]-sum[p])-x[p];        x[pp] += tot[i][go] - sum[pp];        sum[pp]++;        tot[i][go]++;        now = ch[now][go];    }}int main(){    scanf("%d", &T);    while(T--)    {        scanf("%d", &n);        cnt = 0;        ans = 0;        sum[0] = x[0] = 0;        memset(tot, 0, sizeof(tot));        memset(ch[0], 0, sizeof(ch[0]));        for(int i=1; i<=n; i++){            int v;            scanf("%d", &v);            for(int j=30; j>=0; j--){                b[j] = v&1;                v >>= 1;            }            insert();        }        printf("%lld\n", ans);    }    return 0;}