246.数对子

Description

我们定义一个数对 (x,y) 是好的,当且仅当 xy,且 x xor y的二进制表示下有奇数个 1

现在给定 nn 个区间 [li,ri],你需要对于每个 i[1,n],输出有几对好的数 (x,y)满足 x 和 y 都在 [l1,r1][l2,r2]...[li,ri],即两个数都在前 i 个区间的并里

Input

第一行一个正整数 n

接下来 n 行每行两个整数 [li,ri],表示第 i个区间,保证 liri

Output

输出 n 行,第 i行一个整数表示有几对好的数 (x,y) 满足 x,y 都在前 i 个区间的并里

Sample Input

3

1 7

3 10

9 20

Sample Output

12

25

100

Hint

对于 30%30% 的数据,有 1n1001≤n≤100,1liri1001≤li≤ri≤100

对于 50%50% 的数据,有 1n10001≤n≤1000,1liri23211≤li≤ri≤232−1

对于 100%100% 的数据,有 1n1051≤n≤105, 1liri23211≤li≤ri≤232−1

时间限制:2s

空间限制:512MB

Solution

先补充几个小知识点(快速求出一个数的二进制中有多少个1):

x=x&(x-1)(递归求法,适用于单个数)
 
表达式的意思就是:把x的二进制表示 从低位开始,将遇到的第一个为1的 二进制位 置0。

int calc=0;
while(x) x=x&(x-1),calc++;
calc即为所求值

 求0到x中有多少二进制含1个数为奇数的:

long long calc(long long x)
{
    long long tmp=x,tot=0;
    while(tmp)
    {
        if(tmp&1)tot++;
        tmp>>=1;
    }
    return (x>>1)+((x&1) || (tot&1));
}
证明:00 01 10 11 100 101 110 111....继续下去可以发现规律是偶奇奇偶奇偶偶奇....
所以x>>1之前一半的,如果x为奇数(会少算一个)或其本身有奇数个1得加上

p.s.当线段树叶子节点有n个时,应开总共2^(log2n+1)个点,即2*n个点

 正经题解开始:

首先,对于每个数对(x,y), 若要x xor y的二进制表示下有奇数个 1,则必定一者含奇数个1,一者含偶数个。

证明:若两个都为奇数,1.则奇减奇等于偶(重叠个数为奇个)2.奇减偶先为奇(重叠个数为偶数个),奇加奇等于偶

           若两个都为偶数,则可同上证明

           一奇一偶,1.奇减奇等于偶,偶减奇等于奇,奇加偶等于奇2.奇减偶等于奇,偶减偶等于偶,偶加奇等于奇

所以我们采取线段树来维护区间含奇数个1和含偶数个1的个数,对于区间l,r,则用上述中所介绍的calc函数,来calc(r)-calc(l-1)得到奇数个1个数以及r-l+1-(calc(r)-calc(l-1))得到偶数个1个数

每次输入一个区间加进去统计一个区间,然后输出总的相乘即可。p.s.线段树很好的解决了区间相交的问题,在以及统计过的区间标记vis[now]=1;

上代码:

 1 #include<bits/stdc++.h>
 2 #define maxn 100005
 3 using namespace std;
 4 typedef long long ll;
 5 ll num[maxn<<4][2];
 6 int lc[maxn<<4],rc[maxn<<4],rt=0,np=0;
 7 int n;
 8 bool vis[maxn<<4];
 9 void upload(int now){
10     num[now][0]=num[lc[now]][0]+num[rc[now]][0];
11     num[now][1]=num[lc[now]][1]+num[rc[now]][1];
12 }
13 ll calc(ll x)
14 {
15     ll t=x,tot=0;
16     while(t)
17     {
18         if(t&1)tot++;
19         t>>=1;
20     }
21     return (x>>1)+((x&1) || (tot&1));
22 }
23 void update(int &now,ll l,ll r,ll x,ll y){
24     if(!now) now=++np;
25     if(vis[now]) return;
26     if(l>=x&&r<=y){
27         num[now][1]=calc(r)-calc(l-1);
28         num[now][0]=r-l+1-num[now][1];
29         vis[now]=1;
30         return;
31     }
32     ll m=(l+r)>>1;
33     if(y<=m)update(lc[now],l,m,x,y);
34     else if(x>m)update(rc[now],m+1,r,x,y);
35     else{
36         update(lc[now],l,m,x,y);
37         update(rc[now],m+1,r,x,y);
38     }
39     
40     upload(now);
41 }
42 void init(){
43     scanf("%d",&n);
44     ll L,R,mx=1ll<<32;
45     for(int i=1;i<=n;i++){
46         scanf("%lld%lld",&L,&R);
47         update(rt,1,mx,L,R);
48         printf("%lld\n",num[1][0]*num[1][1]);
49     }
50 }
51 int main(){
52     init();    
53     
54     return 0;
55 }
posted @ 2018-09-21 10:15  degage  阅读(335)  评论(0编辑  收藏  举报