HDU 6059-Kanade's trio(多校训练第三场->01字典树)

Kanade's trio

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 711    Accepted Submission(s): 255


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.

1T20

1n5105

0A[i]<230
 

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 n integers 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
 

Source
 

Recommend


题意:

给你n个数,选择三个数按顺序,前2个数的异或小于后两个数的异或值,问你能找到多少组满足提议要求的数对。

题解:

感觉01异或题大部分都需要字典树搞搞,毕竟复杂度在哪摆着的,本题可以这样讨论,无非就两种情况:

(1)假如i和j的第p位是0,则k的第p为要是1,并且p是j和k的最高不相同位,此时满足要求,计入贡献。

(2)假如i和k的最高不相同位p中,i和j在第p位上数值相同,则也满足要求,并计入贡献。

第一种很好处理,定义个一个二维数组cnt[i][j]:表示第i位为j的数的个数。则第一种的贡献值便是cnt[i][1-j]*(cnt[i][1-j]-1)/2,这里很好弄懂,就不过多解释了。

第二种情况就比较麻烦了,因为需要考虑顺序(不是说第一种不用考虑,第一种按照组合数的定义其实就是把顺序考虑在内的),然而第二种就很麻烦了,因为此时要找的i和j不在一个子树中,并且j必须要在i之后,看了大神的思路,可以这样搞,考虑将要加入的数当做i,则前边所有能和i构成i/j关系的数都被当做不合法情况,故字典树存一个ext值表示该数之前所有能与该数组成i/j关系的数的个数,最后在统计贡献时减掉就好了。

#include<map>            
#include<stack>            
#include<queue>          
#include<vector>            
#include<math.h>      
#include<time.h>    
#include<stdio.h>          
#include<iostream>        
#include<string.h>            
#include<stdlib.h>            
#include<algorithm>            
using namespace std;            
typedef long long  ll;            
#define inf 2147483647           
#define mod 1000000007         
#define maxn  5000005        
#define lowbit(x) (x&-x)            
#define eps 1e-10    
int num[32],cnt[32][2],size;
ll ans,ext;
struct node
{
	int next[2];
	int cnt,ext;
}Trie[maxn];
void work(int tmp,ll c)
{
	ans+=1ll*(Trie[tmp].cnt-1)*Trie[tmp].cnt/2;
	ext+=1ll*(c-Trie[tmp].cnt)*Trie[tmp].cnt-Trie[tmp].ext;
}
void insert()
{
	int i,tmp=0;
	for(i=0;i<=29;i++)     
	{
		if(!Trie[tmp].next[num[i]])
			Trie[tmp].next[num[i]]=++size;
		if(Trie[tmp].next[1-num[i]])
			work(Trie[tmp].next[1-num[i]],cnt[i][1-num[i]]);
		tmp=Trie[tmp].next[num[i]];Trie[tmp].cnt++;
		Trie[tmp].ext+=cnt[i][num[i]]-Trie[tmp].cnt;
	}
}
int  main(void)
{
	int T,i,j,n,x;
	scanf("%d",&T);
	while(T--)
	{
		ans=ext=0;size=0;
		memset(cnt,0,sizeof(cnt));
		memset(Trie,0,sizeof(Trie));
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			for(j=29;j>=0;j--)
			{
				num[j]=x%2;
				cnt[j][x%2]++;
				x/=2;
			}
			insert();
		}
		printf("%lld\n",ans+ext);
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值