『异或与高斯消元』

异或与高斯消元

前置知识

Xor and 高斯消元

引入

相信高斯消元大家已经学会

也就是可以解型如下式的n元1次方程组了

\[ \left\{ \begin{aligned} a_{1,1}x_1 + a_{1,2}x_2 + …… + a_{1,n}x_n & = & b_1 \\ a_{2,1}x_1 + a_{2,2}x_2 + …… + a_{2,n}x_n & = & b_2 \\ …… \\ a_{i,1}x_1 + a_{i,2}x_2 + …… + a_{i,n}x_n & = & b_i \\ …… \\ a_{n,1}x_1 + a_{n,2}x_2 + …… + a_{n,n}x_n & = & b_n \\ \end{aligned} \right. \]

现在我改变一下形式 变换成

\[ \left\{ \begin{aligned} a_{1,1}x_1 \oplus a_{1,2}x_2 \oplus …… \oplus a_{1,n}x_n & = & b_1 \\ a_{2,1}x_1 \oplus a_{2,2}x_2 \oplus …… \oplus a_{2,n}x_n & = & b_2 \\ …… \\ a_{i,1}x_1 \oplus a_{i,2}x_2 \oplus …… \oplus a_{i,n}x_n & = & b_i \\ …… \\ a_{n,1}x_1 \oplus a_{n,2}x_2 \oplus …… \oplus a_{n,n}x_n & = & b_n \\ \end{aligned} \right. \]

虽然说$\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\)是这个灯泡本身 因为这个灯泡肯定影响自己

那么我们又可以得到式子了

\[ \left\{ \begin{aligned} a_1 \oplus c_{1,1}x_1 \oplus c_{1,2}x_2 \oplus …… \oplus c_{1,n}x_n & = & b_1 \\ a_2 \oplus c_{2,1}x_1 \oplus c_{2,2}x_2 \oplus …… \oplus c_{2,n}x_n & = & b_2 \\ …… \\ a_i \oplus c_{i,1}x_1 \oplus c_{i,2}x_2 \oplus …… \oplus c_{i,n}x_n & = & b_i \\ …… \\ a_n \oplus c_{n,1}x_1 \oplus c_{n,2}x_2 \oplus …… \oplus c_{n,n}x_n & = & b_n \\ \end{aligned} \right. \]

由于在二阶域上加减是等价的 所以移一下项

\[ \left\{ \begin{aligned} c_{1,1}x_1 \oplus c_{1,2}x_2 \oplus …… \oplus c_{1,n}x_n & = & a_1 \oplus b_1 \\ c_{2,1}x_1 \oplus c_{2,2}x_2 \oplus …… \oplus c_{2,n}x_n & = & a_2 \oplus b_2 \\ …… \\ c_{i,1}x_1 \oplus c_{i,2}x_2 \oplus …… \oplus c_{i,n}x_n & = & a_i \oplus b_i \\ …… \\ c_{n,1}x_1 \oplus c_{n,2}x_2 \oplus …… \oplus c_{n,n}x_n & = & a_n \oplus b_n \\ \end{aligned} \right. \]

由于$ 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) ;
       }
   }
} 
posted @ 2021-08-21 07:52  xxcxu  阅读(386)  评论(0)    收藏  举报