binary trick
binary trick
几乎没用的东西
学了 ,不过感觉还是直接分块预处理的方法(比较)快,而且好打
先直接上代码
namespace binary_trick
{//almost in AC0
typedef unsigned int u32;
typedef unsigned long long ull;
#define p2(x) (1ull<<(ull)(x))
#define bget(x,p) (((x)&p2(p))>>p)
#define bgets(x,l,r) (((x)&all_1[r])>>(l))
const u32 blen=16,hfblen=blen>>1;
const u32 base=(1<<blen)-1,hfbase=(1<<(blen>>1))-1;
const ull max_u32=p2(32)-1;
#define MAX_U32 max_u32
u32 pcnt[base+5],rv[base+5],bcl[base+5],bct[base+5];
ull all_1[65];
inline u32 naive_rev(u32 x)
{
static bool b[8];
irep(i,0,7) b[i]=bget(x,i);
irep(i,0,3) swap(b[i],b[7-i]);
u32 r=0;
irep(i,0,7) if(b[i]) r|=p2(i);
return r;
}
inline void init()
{
pcnt[0]=0,pcnt[1]=1;
irep(i,2,base) pcnt[i]=pcnt[i>>1]+(i&1);
irep(i,0,hfbase) rv[i]=naive_rev(i);
irep(i,hfbase+1,base) rv[i]=(rv[i&hfbase]<<hfblen)|rv[i>>hfblen];
irep(i,0,hfbase) rv[i]<<=8u;
bcl[0]=16,bcl[1]=15;
irep(i,2,base) bcl[i]=bcl[i>>1]-1;
bct[0]=0,bct[1]=0;
irep(i,2,base) bct[i]=i&1?0:(bct[i>>1]+1);
all_1[0]=1;
irep(i,1,63) all_1[i]=(all_1[i-1]<<1ull)|1ull;
}
inline u32 hmw(u32 x)
{ return pcnt[x>>16]+pcnt[x&base]; }
inline u32 u64_hmw(ull x)
{ return pcnt[x>>48]+pcnt[(x>>32)&base]+pcnt[(x>>16)&base]+pcnt[x&base]; }
inline u32 u32_rev(u32 x)
{ return (rv[x&base]<<16u)|rv[x>>16u]; }
inline ull u64_rev(ull x)
{ return (ull)(u32_rev(x>>32ull)|((ull)(u32_rev(x&MAX_U32))<<32ull)); }
inline void u32_print(u32 x,char c=' ')
{ cerr<<(bitset<32>)x<<c; }
inline void u64_print(ull x,char c=' ')
{ cerr<<(bitset<64>)x<<c; }
inline u32 u32_clz(u32 x)
{ return x>base?bcl[x>>16]:(bcl[x&base]+16); }
inline u32 u64_clz(ull x)
{
if(x>>48>0) return bcl[x>>48];
if(x>>32>0) return 16u+bcl[(x>>32)&base];
return 32u+u32_clz(x&MAX_U32);
}
inline u32 u32_ctz(u32 x)
{ return x&base?bct[x&base]:(16u+bct[x>>16u]); }
inline u32 u64_ctz(ull x)
{
if(x&base) return bct[x&base];
if((x>>16ull)&base) return bct[(x>>16ull)&base]+16u;
return u32_ctz(x>>32ull)+32u;
}
inline u32 lowbit(u32 x) { return 1u<<u32_ctz(x); }
inline ull lowbit(ull x) { return p2(u64_ctz(x)); }
inline u32 highbit(u32 x) { return 1u<<(31u-u32_clz(x)); }
inline ull highbit(ull x) { return p2(63ull-u64_clz(x)); }
inline bool add_in(ull x,ull y)
{ return !bcl[x>>48ull]&&!bcl[y>>48ull]; }//判断加法进位
class u128
{
public:
ull high,low;
u128()=default;
u128(int x) { low=x>0?x:-x; }
u128(u32 x) { low=x,high=0; }
u128(ull x) { low=x,high=0; }
u128(ull hb,ull lb) { low=lb,high=hb; }
~u128()=default;
inline void part_print() { cerr<<'<'<<high<<','<<low<<'>'; }
inline u128 friend operator & (u128 a,u128 b) { return u128(a.high&b.high,a.low&b.low); }
inline void friend operator &=(u128& a,u128 b) { a.low&=b.low,a.high&=b.high; }
inline u128 friend operator | (u128 a,u128 b) { return u128(a.high|b.high,a.low|b.low); }
inline void friend operator |=(u128& a,u128 b) { a.low|=b.low,a.high|=b.high; }
inline u128 friend operator ^ (u128 a,u128 b) { return u128(a.high^b.high,a.low^b.low); }
inline void friend operator ^=(u128& a,u128 b) { a.low^=b.low,a.high^=b.high; }
inline bool friend operator ! (u128 a) { return !(a.low|a.high); }
inline u128 friend operator ~ (u128 a) { return u128(~a.high,~a.low); }
inline u128 friend operator <<(u128 a,u32 w)
{
if(!w) return a;
if(w>127u) { return u128(0ull,0ull); }
u128 c(0ull,0ull);
if(w<64u)
{
c.high=(a.low>>(ull)(64u-w))|(a.high<<(ull)w);
c.low=a.low<<(ull)w;
}
else c.low=0ull,c.high=(bgets(a.low,0,127u-w))<<(w-64ull);
return c;
}
inline void operator <<=(u32 w)
{
if(!w) return;
if(w>127u) { low=high=0ull; return; }
if(w<64u) high=(low>>(ull)(64u-w))|(high<<(ull)w),low=low<<(ull)w;
else high=(bgets(low,0,127u-w))<<(w-64ull),low=0ull;
}
inline u128 friend operator >> (u128 a,u32 w)
{
if(!w) return a;
if(w>127u) return u128(0ull,0ull);
u128 c(0ull,0ull);
if(w<64u)
c.high=a.high>>(ull)w,
c.low=(a.low>>(ull)w)|((a.high&all_1[w])<<(64ull-w));
else c.high=0ull,c.low=a.high>>(w-64u);
return c;
}
inline void operator >>=(u32 w)
{
if(!w) return;
if(w>127u) { high=low=0ull; return; }
if(w<64u) low>>=(ull)w,low|=(high&all_1[w])<<(64ull-w),high>>=(ull)w;
else low=high>>(w-64u),high=0ull;
}
inline bool friend operator ==(u128& a,u128& b)
{ return a.low==b.low&&a.high==b.high; }
inline bool friend operator < (u128& a,u128& b)
{ return a.high<b.high?1:(a.high==b.high?a.low<b.low:0); }
inline bool friend operator > (u128& a,u128& b)
{ return a.high>b.high?1:(a.high==b.high?a.low>b.low:0); }
inline bool friend operator <=(u128& a,u128& b)
{ return a.high<b.high?1:(a.high==b.high?a.low<=b.low:0); }
inline bool friend operator >=(u128& a,u128& b)
{ return a.high>b.high?1:(a.high==b.high?a.low>=b.low:0); }
inline bool friend operator !=(u128& a,u128& b)
{ return !(a==b); }
inline u128 friend operator + (u128 a,u128 b)
{
u128 c;
if(!bcl[a.low>>48ull]&&!bcl[b.low>>48ull])
{ c.low=a.low+b.low,c.high=a.high+b.high+1ull; }
else { c.low=a.low+b.low,c.high=a.high+b.high; }
return c;
}
inline void friend operator +=(u128& a,u128 b)
{
if(!bcl[a.low>>48ull]&&!bcl[b.low>>48ull])
{ a.low=a.low+b.low,a.high=a.high+b.high+1ull; }
else { a.low=a.low+b.low,a.high=a.high+b.high; }
}
inline u128 friend operator - (u128 a,u128 b)
{
u128 c;
if(a.low<b.low) c.high=a.high-b.high-1,c.low=a.low+(compl b.low)+1;
else c.high=a.high-b.high,c.low=a.low-b.low;
return c;
}
inline void friend operator -=(u128& a,u128 b)
{
if(a.low<b.low) a.high=a.high-b.high-1,a.low=a.low+(compl b.low)+1;
else a.high=a.high-b.high,a.low=a.low-b.low;
}
inline u128 friend operator * (u128 a,u128 b)
{
return u128(a.high*b.high,a.low*b.low);
}//it's not suggested
operator bool () const { return (bool)(high|low); }
inline void friend u128_print(u128 a);
inline u128 friend u128_rev(u128 x);
inline u32 friend u128_clz(u128 x);
inline u32 friend u128_ctz(u128 x);
inline u32 friend u128_hmw(u128 x);
inline u128 friend u128_lowbit(u128 x);
inline u128 friend u128_highbit(u128 x);
};
const u128 unit_128(0ull,1ull);
inline void u128_print(u128 a) { cerr<<(bitset<64>)a.high<<(bitset<64>)a.low; }
inline u128 u128_rev(u128 x) { return u128(u64_rev(x.low),u64_rev(x.high)); }
inline u32 u128_clz(u128 x) { return x.high?u64_clz(x.high):64u+u64_clz(x.low); }
inline u32 u128_ctz(u128 x) { return x.low?u64_ctz(x.low):u64_ctz(x.high)+64u; }
inline u32 u128_hmw(u128 x) { return u64_hmw(x.low)+u64_hmw(x.high); }
inline u128 u128_lowbit(u128 x) { return unit_128<<u128_ctz(x); }
inline u128 u128_highbit(u128 x){ return unit_128<<(127u-u128_clz(x)); }
inline u128 turtle_mul(u128 a,u128 b)
{
u128 c(0ull,0ull);
while(b)
{
if(b&unit_128) c+=a;
a<<=1u,b>>=1u;
} return c;
}
inline u128 turtle2_mul(u128 a,u128 b)
{
u32 tmp;
u128 c(0ull,0ull);
while(b)
{
tmp=u128_ctz(b);
a<<=tmp;
c+=a;
b>>=tmp+1;
a<<=1u;
} return c;
}
inline u128 slow_mul(u128 A,u128 B)
{
static u32 a[4],b[4];
static ull tmp;
a[0]=A.low&MAX_U32,a[1]=A.low>>32ull,a[2]=A.high&MAX_U32,a[3]=A.high>>32ull;
b[0]=B.low&MAX_U32,b[1]=B.low>>32ull,b[2]=B.high&MAX_U32,b[3]=B.high>>32ull;
u128 c( (ull)a[1]*(ull)b[1] , (ull)a[0]*(ull)b[0] );
tmp=1ull*a[0]*b[1];
c.high+=(tmp>>32ull)+add_in(c.low,(tmp&MAX_U32)<<32ull);
c.low+=(tmp&MAX_U32)<<32ull;
tmp=1ull*a[1]*b[0];
c.high+=(tmp>>32ull)+add_in(c.low,(tmp&MAX_U32)<<32ull);
c.low+=(tmp&MAX_U32)<<32ull;
tmp=1ull*a[2]*b[0]+1ull*a[0]*b[2];
c.high+=tmp;
tmp=1ull*a[2]*b[1]+1ull*a[1]*b[2];
c.high+=(tmp&MAX_U32)<<32ull;
tmp=1ull*a[3]*b[0]+1ull*a[0]*b[3];
c.high+=(tmp&MAX_U32)<<32ull;
return c;
}//it's the suggested way to get a*b
} namespace efX_bt=binary_trick;
可以用 了,但是乘法挺慢的,而且没有除法,所以不能输出,毕竟本来就是给你当 用的,不过手动循环展开的好像还是快一点,不过不知道这个和编译器版本有没有关系
预处理的时间不到 ,空间不到 ,应该没有什么会被卡的地方
命名的规范是 表示对应的函数
有
完全没用的东西
要计算复杂度,我们需要假设我们做那些操作是 的,这个就是我们采用的
一个 不能过于不现实,比如我们一般假设 是 的运算,但在高精的情形下这就显然不合理
不合理的原因是没有考虑位长 ,也就是我们一个 unsigned int
或者 unsigned long long
能存储的二进制位数
考虑了之后,我们认为 是 的,之所以说是认为,是因为 其实不能做到 ,不过这个就涉及到比较底层的设计了,和算法关系不大,因为计算机算乘法也很快,所以就认为 也是 的了
考虑到现在的 , 一般取 或
这个 就叫
还有一个常见的(一般是默认的)假设是 ,要不然我们取地址都不是 的,就感觉不太能做
这些操作被称为 基类,因为还可以扩展出一些其他的操作也是 的
clz&ctz
这个东西直接做是不能做到 的,最 的就是按位枚举计数,这样是 的
然后二分就可以做到
但如果我们预处理,那么就可以做到 求 (当然不是上面那种分段打表的做法)
我们从 向 和 连边,然后跑欧拉回路,可以在 的时间内制备出一张类似 表的东西,问的时候就可以 查 了
因为我们假设了 ,所以 的制备时间认为白给的
然后 都做了,对于 ,我们只需要 求出 就可以了,写过树状数组就知道这是 ,不过我们一般认为 是 unsigned
的,所以还需要手动模拟一下补码的转化
popcount
这个又叫 ,也是我代码中的命名
直接枚举显然也是 的
然后可以分治,以 为块长分块,每次合并使块长变为 ,块内记录块内 的个数,一个块的值域是 ,显然不会溢出
这样需要构造出形如 这样的数,即 个 后接一个 ,预处理是 的,但是发现分治过程就是这个反过来,所以其实不用预处理,可以在线制备,依然是 的(不过这么慢的写法不会有人用就是了)
考虑块的值域是 而最后答案不超过 ,所以试着在 时就停止分治
之后我们假设问题是在一个序列上,我们有 表示每个块内的答案,我们要求 ,最简单的做法就是卷积,而在一个 内卷积是 的,这时我们发现最后一个块内的值就是答案
既然最后一次可以用卷积提速,那之前的操作也可以用卷积提速,把每次分治合并变为卷积合并,这样每次块长从 变为 ,只会迭代 次( 表示反阿克曼函数)
被证明了不能在 里 的(不考虑分段打表这种需要 的预处理时间的做法,这样的预处理时间在理论上不可接受)
pack&unpack
我们想把一堆分开的数放在一起(),或者进行相反的操作()
对于数的规模有要求,假设我们有 个块,每个块的末尾有 的位置存储了我们想要的数据,我们只需要构造出 (有 个 ,相邻的 的距离为 )
原数与 卷积的最高位的块就是我们需要的答案
不难发现再卷一次就可以 了,不过需要忽略无关位,这个预处理出无关位是 ,有关位为 的数就可以了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现