Day1 A 数对子 [找规律]

Day1 A 数对子 [找规律]

懒得粘题面了…..

这里写图片描述

题解

错解

我最开始打了三个小时的表,发现了一件神奇的事情: [ 1 , k ] 内的“好的”数对的数量是有规律可循的(这个规律极其复杂)!所以每个区间内的“好的”数对的数量就是 A [ r i ] A [ l i 1 ] ,然后我就愉快地切过此题啦!

这当然是很naive的错解。一个数对如果横跨两个区间,那怎么减?

打表留念:

这里写图片描述

正解

当把某一位上的1异或掉的时候,1的总个数会减少2个。

如果要得到奇数个1,那么x和y的1的总个数加起来必须是奇数,这样无论抵消掉多少个1,最后的答案中1的个数也是奇数。所以x,y中只要一个是奇数,一个是偶数就可以满足要求。

打表可得:当 k 为奇数的时候, [ 1 , k ] 内有奇数个 1 的数字个数为 ( k + 1 ) / 2 ;当 k 为偶数时, [ 1 , k 1 ] 内的奇数个数为 k / 2 ,再特判一下 k 即可。

最后线段树维护区间的并,查询时只用查询1号线段的答案。

注意被完全覆盖过的线段要做标记。

代码

#include<iostream>
#include<cstdio>
#define N 4001000
#define ll long long
using namespace std;
ll bitcnt(ll x){
    ll Ans=0;
    while(x)x^=x&(-x),Ans++;
    if(Ans&1LL)return 1LL;return 0;
}
ll Find(ll x){
    if(x&1LL)return (x+1)>>1LL;
    return (x>>1LL)+bitcnt(x);
}
ll Odd[N],Even[N],Lson[N<<4],Rson[N<<4],Tot;
void Add(ll p,ll x,ll y){
    Odd[p]=Find(y)-Find(x-1);
    Even[p]=y-x+1-Odd[p];
}
bool Mark[N];
void Ins(ll &p,ll l,ll r,ll x,ll y){
    if(y<l || x>r)return;
    if(Mark[p])return;
    if(!p)p=++Tot;
    if(x<=l && y>=r){Mark[p]=true,Add(p,l,r);return;}
    ll mid=l+r>>1LL;
    Ins(Lson[p],l,mid,x,y);
    Ins(Rson[p],mid+1,r,x,y);
    Odd[p]=Odd[Lson[p]]+Odd[Rson[p]];
    Even[p]=Even[Lson[p]]+Even[Rson[p]];
}
int main(){
    ll n;scanf("%lld",&n);
    for(ll i=1;i<=n;i++){
        ll l,r;scanf("%lld%lld",&l,&r);
        ll Root=0;if(i!=1)Root=1;
        Ins(Root,1,(1LL<<32)-1,l,r);
        printf("%lld\n",Odd[1]*Even[1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ArliaStark/article/details/81506667