[LOJ6540]无聊的数对

题目

传送门

题解

膜拜机房大佬 \(\text{JZM}\) 当场切掉。

\(30pts\) 方法

首先,对于 \(n\le 100,1\le l_i\le r_i\le 100\) 的数据,我们显然可以暴力,能打出以下代码便可以得到 \(30pts\) 了。

const int MAXN=100;
const int MAXR=1e3;

bool vis[MAXR+5][MAXR+5];

int bitcnt[MAXR+5];

struct node{int l,r;}q[MAXN+5];
int n;
LL ans;

signed main(){
    rep(i,1,MAXR)bitcnt[i]=bitcnt[i>>1]+(i&1);
    n=qread(1);
    rep(i,1,n)q[i].l=qread(1),q[i].r=qread(1);
    rep(t1,1,n){
        rep(t2,1,t1){
            rep(i,q[t1].l,q[t1].r)rep(j,q[t2].l,q[t2].r)if(bitcnt[i^j]&1 && !vis[i][j])
                ++ans,vis[i][j]=vis[j][i]=true;
        }
        writc(ans,'\n');
    }
    return 0;
}

\(100pts\) 方法

要过这道题,首先你需要足够深入的对于 \(\texttt{xor}\) 的认识。

考虑两个数 \(x\oplus y\) 能得到奇数个 \(1\) 的情况是什么?

似乎我们可以很容易地进行判断,只需要 \(x\)\(y\) 所错开的 \(1\) 的个数为奇数。

有没有更简便的说法?我们给出结论

只需要 \(x\)\(y\)\(1\) 的个数奇偶性不同,\(x\oplus y\) 便可得到奇数个 \(1\),否则是不可能的。

这个结论显然得证,此处简要说明一下:

考虑将 \(y\) 异或进 \(x\) 中,如果 \(y\) 的某一位为 \(1\),那么 \(x\) 对应的位数就会进行一次取反操作,而一次去翻操作会对 \(1\) 的个数 \(\pm 1\),如果 \(y\) 有偶数个 \(1\),那么 \(x\) 对应地就有偶数个位会取反,相当于 \(x\)\(1\) 变化了偶数个,而如果 \(x\) 自身存在的 \(1\) 个数也为偶数,最后得到的异或结果的 \(1\) 也只可能为偶数。

那么,我们需要找的就是什么?就是交区间中,有偶数个 \(1\) 的数和有奇数个 \(1\) 的数的个数,作乘积之后 \(/2\) 即可。

显然,奇数 \(1\) 和偶数 \(1\) 的数是成对出现的,我们可以 \(\mathcal O(1)\) 地得到一个区间中两种数的个数。

同样,如果一个区间中有 \(n\) 个数,偶数个 \(1\) 的数有 \(k\) 个,那么另一种数就有 \(n-k\) 个,这是显然的。

因为询问是由区间构成,我们可以维护两棵线段树,分别维护区间数字总数与区间中有偶数(奇数也可以,看个人喜好)个 \(1\) 的数的个数,然后 \(\mathcal O(1)\) 输出答案即可。

#include<bits/stdc++.h>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#ifdef _GLIBCXX_CSTDIO
#define cg (c=getchar())
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T qread(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
#undef cg
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
#endif
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=1e5;
const int logMAXN=16;
const int MAXSIZE=2*logMAXN*MAXN;

int ncnt,rt;
int sum[MAXSIZE+5],even[MAXSIZE+5];
int lc[MAXSIZE+5],rc[MAXSIZE+5];
bool tag[MAXSIZE+5];
#define mid ((l+r)>>1)
#define _lq lc[i],l,mid
#define _rq rc[i],mid+1,r

inline void pushup(const int i){
    sum[i]=sum[lc[i]]+sum[rc[i]];
    even[i]=even[lc[i]]+even[rc[i]];
}

inline void all_full(const int i,const LL l,const LL r){
    tag[i]=true;
    sum[i]=r-l+1;
    even[i]=(r-l+1)>>1;
}

void add(int& i,const LL l,const LL r,const LL L,const LL R){
    if(i==0)i=++ncnt;
    if(l==r){//单点特殊处理
        sum[i]=1;
        even[i]=!(__builtin_popcount(l)&1);
        // printf("l == %d, even == %d\n",l,even[i]);
        return;
    }
    if(tag[i])return;//之前已经全部覆盖过了,不用重复覆盖
    if(L<=l && r<=R){//如果是一个区间,那么可以直接算
        all_full(i,l,r);
        return;
    }
    if(L<=mid)add(_lq,L,R);
    if(mid<R)add(_rq,L,R);
    pushup(i);
}

LL l,r;

signed main(){
    rep(tmp,1,qread(1)){
        l=qread(1ll),r=qread(1ll);
        add(rt,0,(1ll<<32)-1,l,r);
        printf("%lld\n",1ll*(sum[rt]-even[rt])*even[rt]);
    }
    return 0;
}
posted @ 2020-06-13 14:36  Arextre  阅读(163)  评论(0编辑  收藏  举报