[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;
}