您的位置:首页 > 大数据 > 人工智能

2017 Multi-University Training Contest - Team 3:1004. Kanade's trio(01字典树)

2017-08-04 00:19 525 查看




感觉很多时候字典树都是这种题的万能解

(具体规则or什么是字典树:http://blog.csdn.net/jaihk662/article/details/53930927

建立两棵01字典树,对于当前第j个节点,将前面所有数加入第一棵字典树,将后面所有数加入第二棵字典树

之后查询就好了,如果两个数a[i]和a[k]前x位都相同,那么很显然这两个数异或a[j]前x位也都相同

所以你只要找出a[i]和a[k]第一个不同的地方进行异或比较就好

在字典树上的操作就是:对于两棵字典树所有相同的前缀,答案加上两棵树下一个不同的节点的权值积

例如:两棵树都经过了10010(root->R->L->L->R->L),那么

ans += sum1(100101)*sum2(100100)或者ans += sum1(100100)*sum2(100101)(具体加哪一个取决于a[j])

但相同前缀个数可能过多,所以要在树上求后缀

tre[]:字典树

val[now][t]:第t棵字典树now节点的权值(t=0那棵用来存前面的数)

frd[x]:节点x的兄弟(x和frd[x]父亲是同一个)

rek[i][]:存后缀,前i-1位都相同第i个数不同对答案的贡献

#include<stdio.h>
#include<string.h>
#define LL long long
int root, tre[3000055][2], val[3000055][2], rek[35][2], a[560000];
LL frd[3000055];
void Insert(LL x, int t)
{
int i, c, now = 0;
for(i=30;i>=0;i--)
{
c = 0;
if(x&(1<<i))
c = 1;
if(tre[now][c]==0)
{
tre[now][c] = ++root;
if(tre[now][c^1])
{
frd[root] = tre[now][c^1];
frd[tre[now][c^1]] = root;
}
}
now = tre[now][c];
val[now][t] += 1;
rek[i][c^t] += val[frd[now]][t^1];
}
}
void Delete(LL x, int t)
{
int c, i, now = 0;
for(i=30;i>=0;i--)
{
c = 0;
if(x&(1<<i))
c = 1;
now = tre[now][c];
val[now][t] -= 1;
rek[i][c^t] -= val[frd[now]][t^1];
}
}
LL Query(LL x)
{
int i, c;
LL sum = 0;
for(i=30;i>=0;i--)
{
c = 0;
if(x&(1<<i))
c = 1;
sum += rek[i][c];
}
return sum;
}
int main(void)
{
LL ans;
int T, n, i;
scanf("%d", &T);
while(T--)
{
ans = root = 0;
memset(tre, 0, sizeof(tre));
memset(val, 0, sizeof(val));
memset(rek, 0, sizeof(rek));
memset(frd, 0, sizeof(frd));
scanf("%d", &n);
for(i=1;i<=n;i++)
scanf("%d", &a[i]);
for(i=2;i<=n;i++)
Insert(a[i], 1);
Insert(a[1], 0);
for(i=2;i<=n;i++)
{
Delete(a[i], 1);
ans += Query(a[i]);
Insert(a[i], 0);
}
printf("%lld\n", ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐