线性基
https://www.luogu.com.cn/problem/solution/P3812
洛谷讲解链接
点击查看代码
int cnt;//记录线性基个数,数集确定,cnt确定
inline void ins(ll x)
{
f_(i,62,0)
{
if(x&(1ll<<i))
{
if(!p[i])
{
p[i]=x;break;
}
x^=p[i];//保证线性基如果能插入一定是可以被原有数异或出来
//的
}
}
}
inline int ask_exist(ll x)//判断x能否被已有数集表示出来
{
f_(i,62,0)
{
if(x&(1ll<<i))
{
if(!p[i])return 0;//插入成功代表不能被表示
x^=p[i];
}
}
/*这是另一个版本,一样的。
因为假设p[i]==0而且x有这一位,这一位一定会被保留下来从而x不是0,代表
x不能被表示
f_(i,62,0)
if(x&(1ll<<i))x^=p[i];
return x==0;
*/
return 1;
}
inline ll ask_max()//查询子集异或最大值
{
ll ans=0;
f_(i,62,0)
if((ans^p[i])>ans)ans^=p[i];//因为高位贪心不影响低位
return ans;
}
inline ll ask_min()//查询子集异或最小值
{
if(zero)return 0;//因为线性基不能表示出0:
//假设pi^pj^pk==0,那么pi^pj=pk,但是i/j/k的最高位不一致,也就是说
//对于最高位的1假设在i,那么pk的第i位的1消除不掉,但是Pk显然第
//i位没有1,所以矛盾
_f(i,0,62)if(p[i])return p[i];
}
/
inline void rebuild_of_ask_Kth_min()
{
//rebuild:
cnt=0,top=0;
f_(i,MB,0)//必须从高位到低位!
//可以理解成pj要求原始数组
{
f_(j,i-1,0)
if(p[i]&(1ll<<j))p[i]^=p[j];
}
_f(i,0,MB)if(p[i])d[cnt++]=p[i];//新的线性基
}
inline ll ask_Kth_min(int k)//排除了0的异或子集第k小
{
if(k>=(1ll<<cnt))return -1;//取等号,因为
//cnt代表可以不同的位数,本来应该是有2^cnt种构造数的方案
//但是对于每一位都是0的我已经提前考虑过了,所以当k=2^cnt其实是
//不存在的
ll ans=0;
_f(i,0,cnt-1)//对于排名的累加
if(k&(1ll<<i))ans^=d[i];//二进制分解
return ans;
}
inline ll ask_rank(ll x)//查询x在所有数集异或可以表示出来的集合中
//的排名
{
int ans=0;
f_(i,cnt-1,0)if(x>=d[i])ans+=(1ll<<i),x^=d[i];//只考虑
//每一个重建线性基是1的位,x满不满足条件
//其实就是比较对于可表示集合中,如果第i个有意义位是0,更高位
//已经进行完决策(如果x更大,就有2种,x小,就只能让高某位是0)
//对于低位任意的一个排名累加
//如果x>d[i],说明i位0/1都行,否则0一定行,1不行
return ans+zero;
}
int main()
{
chu("%lld",tmp-zero ? ask_Kth_min(tmp-zero):0ll);
}
/
附加百度上搜不到的一个结论:一组数可以表示出来的数个数就是线性基的每一位!=0的个数的2次幂,\(2^{cnt}\):解释:因为线性基中每个数都不能互相异或出来,所以对于每个基选或者不选都会组合出不同的数。
线性基求最大^路径和
(1)起点1终点n
考虑答案组成部分:一条从起点到终点的链+若干个环,把图中所有环的线性基搞出来,任意链扔基里求max。
(2)起点1终点1
若干环