Day1 A 数对子 [找规律]
懒得粘题面了…..
题解
错解
我最开始打了三个小时的表,发现了一件神奇的事情: [1,k] [ 1 , k ] 内的“好的”数对的数量是有规律可循的(这个规律极其复杂)!所以每个区间内的“好的”数对的数量就是 A[ri]−A[li−1] A [ r i ] − A [ l i − 1 ] ,然后我就愉快地切过此题啦!
这当然是很naive的错解。一个数对如果横跨两个区间,那怎么减?
打表留念:
正解
当把某一位上的1异或掉的时候,1的总个数会减少2个。
如果要得到奇数个1,那么x和y的1的总个数加起来必须是奇数,这样无论抵消掉多少个1,最后的答案中1的个数也是奇数。所以x,y中只要一个是奇数,一个是偶数就可以满足要求。
打表可得:当 k k 为奇数的时候,内有奇数个 1 1 的数字个数为;当 k k 为偶数时,内的奇数个数为 k/2 k / 2 ,再特判一下 k k <script type="math/tex" id="MathJax-Element-22">k</script>即可。
最后线段树维护区间的并,查询时只用查询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;
}