zoukankan      html  css  js  c++  java
  • 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
    ",num[1][0]*num[1][1]);
    49     }
    50 }
    51 int main(){
    52     init();    
    53     
    54     return 0;
    55 }
  • 相关阅读:
    django with mysql (part-4)
    django with mysql (part-3)
    django with mysql (part-2)
    [LeetCode] 22. 括号生成(回溯/DP)
    [算法]求满足要求的进制(辗转相除(欧几里得算法),求最大公约数gcd)
    [算法]体积不小于V的情况下的最小价值(0-1背包)
    [LeetCode]96. 不同的二叉搜索树(DP,卡特兰数)
    [LeetCode]98. 验证二叉搜索树
    [LeetCode]21. 合并两个有序链表(递归)
    [LeetCode]538. 把二叉搜索树转换为累加树
  • 原文地址:https://www.cnblogs.com/degage/p/9685123.html
Copyright © 2011-2022 走看看