『异或与高斯消元』
异或与高斯消元
前置知识
Xor and 高斯消元
引入
相信高斯消元大家已经学会
也就是可以解型如下式的n元1次方程组了
现在我改变一下形式 变换成
虽然说$\oplus $是不进位的加法 但是还是很难消元
对于这个我们稍微对高斯消元改一改 具体是这样的
对于\(j \in [1,n]\) 找到一个\(a_{i,j} \neq 0\) 把第i行和第k行交换
再用第第k行异或下面所有的\(a_{i,k} \neq 0\)的行i 其中$ k \in [1,n]$ 消去系数
这样原矩阵后就成了上三角矩阵 下面就和高斯消元差不多了
例题
相信大家都会了 \(/se.jpg\)
那么给出一道例题 开关问题
首先我们对于每一个灯泡的初始状态 开:1 关:0
再有一系列的开关会影响到其他灯泡
我们可以把所有灯泡看作向量\((a_1,a_2,……,a_n)\)
那么第i影响开关可以记为向量\((0,s_1,0,a_i,0……,0,s_2,0)\)
其中\(s_i\)为影响的灯泡 \(a_i\)是这个灯泡本身 因为这个灯泡肯定影响自己
那么我们又可以得到式子了
由于在二阶域上加减是等价的 所以移一下项
由于$ a_i $和 $ b_i $ 已知 那么直接高斯消元
如果对异或高斯消元有疑惑可以看看代码
#include <bits/stdc++.h>
using namespace std ;
const int N = 1e2 + 5 ;
const double eps = 1e-5 ;
int read(int x = 0,bool f = false,char ch = getchar()) {
for (;!isdigit(ch);ch = getchar()) f |= (ch == '-') ;
for (;isdigit(ch);ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48) ;
return f ? ~ x + 1 : x ;
}
int T ;
class Martix {
private :
int n ;
int martix[N][N] ;
public :
Martix(int x){
memset(martix,x,sizeof martix) ;
}
void Init(Martix* &Rt) {
int x , y ;
Rt -> n = read() ;
for (int i = 1 ; i <= Rt -> n ; ++i ) Rt -> martix[i][Rt -> n + 1] = read() ;
for (int i = 1 ; i <= Rt -> n ; ++i ) {
x = read() ;
Rt -> martix[i][Rt -> n + 1] ^= x ;
Rt -> martix[i][i] = 1 ;
}
while (x = read(),y = read(),x || y) Rt -> martix[y][x] = 1 ;
}
int solve(Martix* &Rt) {
int ans = 1 , r = 1 ;
for (int i = 1 ; i <= Rt -> n ; ++i ) {
int mx = i ;
for (int j = r + 1 ; j <= Rt -> n ; ++j )
if (Rt -> martix[j][i])
mx = j ;
if (!(Rt -> martix[mx][i])) continue ;
for (int j = i ; j <= Rt -> n + 1 ; ++j ) swap(Rt -> martix[mx][j],Rt -> martix[r][j]) ;
for (int j = r + 1 ; j <= Rt -> n ; ++j )
for (int k = Rt -> n + 1 ; k >= i ; --k )
Rt -> martix[j][k] ^= Rt -> martix[j][i] & Rt -> martix[r][k] ;
++ r ;
}
if (r < Rt -> n + 1) {
for (int i = r ; i <= Rt -> n ; ++i )
if (Rt -> martix[i][Rt -> n + 1]) return -1 ;
else ans <<= 1 ;
} return ans ;
}
} ;
signed main() {
T = read() ;
while (T --) {
Martix* Answer = new Martix(0) ;
Answer -> Init(Answer) ;
int ans = Answer -> solve(Answer) ;
if (ans == -1) puts("Oh,it's impossible~!!") ;
else printf("%d\n",ans) ;
}
return 0 ;
}
下一题 嘿嘿欸
题目提示的够明显了吧
最大购买装备就高斯消元后的秩的值
然后跑一遍实数域上的线性基(回归了线性基的本质 向量)
#include <bits/stdc++.h>
using namespace std;
int read(int x = 0,bool f = false,char ch = getchar()) {
for (;!isdigit(ch);ch = getchar()) f |= (ch == '-') ;
for (;isdigit(ch);ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48) ;
return f ? ~ x + 1 : x ;
}
const int N = 5e2 + 5 ;
const long double eps = 1e-7 ;
int n , m , ans , tot ;
int p[N] ;
struct equip {
long double attribute[N] ;
int price ;
friend bool operator < (const equip a,const equip b) {
return a.price < b.price ;
}
} Equip[N] ;
signed main() {
n = read() , m = read() ;
for (int i = 1 ; i <= n ; ++i )
for (int j = 1 ; j <= m ; ++j )
scanf("%llf",&Equip[i].attribute[j]) ;
for (int i = 1 ; i <= n ; ++i )
Equip[i].price = read() ;
sort(Equip + 1,Equip + 1 + n) ;
for (int i = 1 ; i <= n ; ++i )
for (int j = 1 ; j <= m ; ++j )
if (fabs(Equip[i].attribute[j]) > eps) {
if (!p[j]) {
p[j] = i ;
++ tot , ans += Equip[i].price ;
break ;
} else {
long double div = Equip[i].attribute[j] / Equip[p[j]].attribute[j] ;
for (int k = j ; k <= m ; ++k )
Equip[i].attribute[k] -= Equip[p[j]].attribute[k] * div ;
}
}
printf("%d %d\n",tot,ans) ;
}
异或线性空间
这玩意不是很水 直接用线性基水过
好吧 还是讲下高斯消元求法
题目先看一遍 异或运算
显然在组成的若干个数\(\oplus\)后一定存在\(a_i = a_j\)
我们需要做的就是去掉那些重复的数
显然在线性空间中如果一个数能被其他数字异或起来得到 即这个数肯定为某些数的线性组合
高斯消元的结果一定为0
所以我们去掉这些数即可
那么接下来要做的就是……
码!
等一等 还有一点注意的 如果所有数字被排除掉 那么还有0的情况 需要判一判
不过大家这么巨 肯定都想到了
以及老人言
"不开longlong见我爹(见祖宗)"
下面是高斯消元代码
#include <bits/stdc++.h>
using namespace std;
const int N = 20000;
int n;
int cnt = 1;
long long a[N];
inline int read(void)
{
int s = 0;char c = getchar();
while (c<'0' || c>'9') c = getchar();
while (c>='0' && c<='9') s = s*10+c-48, c = getchar();
return s;
}
void print(void)
{
for (int i=1;i<=n;++i)
{
int t[100] = {};
int k = a[i], s= 0 ;
while (k) t[++s] = k&1, k >>= 1;
for (int i=4;i;--i) cout<<t[i];
cout<<endl;
}
return;
}
void work(void)
{
bool flag = 0;
n = read();
for (int i=1;i<=n;++i) scanf("%lld",a+i);
for (int i=1;i<=n;++i)
{
for (int j=i;j<=n;++j)
if (a[j]>a[i]) swap(a[i],a[j]);
if (a[i] == 0) { flag = 1; n = i-1; break; }
for (int j=63;j>=0;--j)//63 not 64
{
if ((a[i] >> j) & 1)
{
for (int k=1;k<=n;++k)
if (i ^ k && ((a[k] >> j) & 1)) a[k] ^= a[i];
break;
}
}
}
int m = read();
printf("Case #%d:\n",cnt ++);
for (int i=1;i<=m;++i)
{
long long ans = 0,k;
scanf("%lld",&k);
if (flag == 1) k --;
if (k >= pow(2,n))
{
puts("-1");
continue;
}
for (int j=64;j>=1;--j) //j表示第几位
if (k >> j-1 & 1) ans ^= a[n-j+1];
printf("%lld\n",ans);
}
return;
}
int main(void)
{
int T = read();
while (T --) work();
return 0;
}
当然我们也可以选择用线性基水过这道题
线性基代码 丑的一比 还wa了好久 发现竟然是……数组开小了
其实还是线性基快一点 /se.
所以眼睛放干净的 别老看些吃不消的 听弟弟一句劝 你把握不住 交给弟来
#include <bits/stdc++.h>
using namespace std;
typedef long long LL ;
inline LL read(register LL x=0,register char ch=getchar(),register bool f=false) {
for (;!isdigit(ch);ch=getchar()) if(ch=='-') f=1 ;
for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48) ; return f ? ~x+1 : x ;
}
const LL N = 1e4 + 5 ;
LL T , n , ans , k , q , idx ;
class Linear_basis {
public :
LL a[N] , p[N] , tot ;
void insert (LL x) {
for (LL i = 63 ; ~i ; --i ) {
if ((1LL<<i)&x) {
if (!p[i]) { p[i] = x ; break ; }
else x ^= p[i] ;
}
}
}
void build () {
for (LL i = 63 ; ~i ; --i ) {
if (p[i]) {
for (LL j = i + 1 ; j <= 63 ; ++j ) {
if ((p[j] >> i) & 1) p[j] ^= p[i] ;
}
}
}
for (LL i = 0 ; i <= 63 ; ++i ) {
if (!p[i]) continue ;
p[tot ++] = p[i] ;
} -- tot ;
}
void init () {
for (LL i = 1 ; i <= n ; ++i ) a[i] = read() ;
for (LL i = 1 ; i <= n ; ++i ) insert(a[i]) ;
}
LL kth (LL k) {
if ((1LL << tot + 1LL) <= k) return -1 ;
LL ans = 0 ;
for (LL i = 63 ; ~i ; --i ) if (1LL & (k >> i)) ans ^= p[i] ;
return ans ;
}
};
Linear_basis lb ;
signed main() {
T = read() ;
while(T --) {
memset(lb.p,0,sizeof lb.p) ;
lb.tot = 0 ;
printf("Case #%lld:\n",++ idx) ;
n = read() ;
lb.init() ;
lb.build() ;
q = read() ;
for (LL i = 1 ; i <= q ; ++i ) {
LL x = read() ;
if(n != lb.tot + 1) -- x ;
ans = lb.kth(x) ,
printf("%lld\n",ans) ;
}
}
}
本文来自博客园,作者:xxcxu,转载请注明原文链接:https://www.cnblogs.com/Maraschino/articles/15168781.html