线性基学习

求线性基模板

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1e5 + 10;
int maxbit = 64;
ll a[N],p[N];   //p数组储存所有的线性基
int n;
int main(){
	scanf("%d",&n);
	memset(p,0,sizeof(p));
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=maxbit-1;j>=0;j--){
			if((a[i] >> j) & 1){
				if(p[j] == 0){ 
					p[j] = a[i];
					break;
				}else	a[i] ^= p[j];
			}
		}
	}
	int r = 0;
	for(int i=maxbit-1;i>=0;i--) /
		if(p[i])	r++;
	cout<<r<<endl;   //线性基的个数
	ll k;
	while(cin>>k){
		if(k == 0){
			if(r == n)	cout<<"False"<<endl;
			else	cout<<"True"<<endl;
		}else{
			bool flag = false;
			for(int j=maxbit-1;j>=0;j--){
				if((k>>j) & 1)	k ^= p[j];
				if(k == 0){
					flag = true;
					break;
				}
			}
			if(flag)	cout<<"True"<<endl;
			else	cout<<"False"<<endl;
		}
	}
	return 0;
}

 

luogu3812

题意:求最大能抑或的数

题解:从高位到地位扫描,每次保存最大的数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 50 + 10;
int const maxbit = 64;
ll a[N],p[maxbit];   //p数组储存所有的线性基
int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++){
		for(int j=maxbit-1;j>=0;j--){
			if((a[i] >> j) & 1){
				if(p[j] == 0){ 
					p[j] = a[i];
					break;
				}else{
					a[i] ^= p[j];
				}
			}
		}
	}
	ll ans = 0;
	for(int i=maxbit-1;i>=0;i--){
		if(p[i])	ans = max(ans,ans ^ p[i]);	
	}
	cout<<ans<<endl;
	return 0;
}

HDU3949

题意:求n个数能表示的第k小的数是多少。

题解:找到线性基,化简为最简式。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 10000 + 10;
int const maxbit = 64;
ll a[N],p[N],l[N];
int n,Q;
int main(){
	int T,caser = 0;
	scanf("%d",&T);
	while(T--){
		printf("Case #%d:\n",++caser);
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		scanf("%d",&Q);
		memset(p,0,sizeof(p));
		for(int i=1;i<=n;i++){
			for(int j=maxbit-1;j>=0;j--){
				if((a[i] >> j) & 1){
					if(p[j] == 0){
						p[j] = a[i];
						break;
					}else{
						a[i] ^= p[j];
					}
				}
			}
		}
		for(int i=maxbit-1;i>=0;i--){   //转化为最简式,每一列只有一个1
			for(int j=i+1;j<maxbit;j++){
				if((p[j] >> i) & 1){
					p[j] ^= p[i];
				}
			}
		}
		int r = 0;
		for(int i=0;i<maxbit;i++){
			if(p[i]){
				l[r++] = p[i];
			}
		}
		while(Q--){
			ll k;
			scanf("%lld",&k);
			if(r != n)	k--;   //0可以表示
			if(k >= 1LL << r){
				printf("-1\n");
			}else{
				ll ans = 0;
				for(int j=0;j<maxbit;j++){
					if((k >> j) & 1)
						ans ^= l[j];
				}
				printf("%lld\n",ans);
			}
		}
	}
	return 0;
}

BZOJ2844

题意:对所有子集的抑或结果排序,查找给定的数的排名。

题解:设基的大小为r,那么每个数出现的次数为2^(n-r)。对于给定的数x,计算比x大的数有多。

#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int const maxbit = 64;
int const mod = 10086;
int n;
int a[N],p[maxbit],l[maxbit],Q;
int qpow(int a,int n){
	int ans = 1;
	while(n){
		if(n&1)	ans = ans * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		for(int j=maxbit-1;j>=0;j--){
			if((a[i] >> j) & 1){
				if(p[j] == 0){
					p[j] = a[i];
					break;
				}else{
					a[i] ^= p[j];
				}
			}
		}
	}
	scanf("%d",&Q);
	int r = 0;
	int ans = 0;
	for(int i=0;i<maxbit;i++){
		if(p[i])
			l[i] = r++;
	}
	for(int i=0;i<maxbit;i++){
		if(p[i] && (Q >> i) & 1){
			ans += (1<<l[i]);
		}
	}
	printf("%d\n",(ans % mod * qpow(2,n-r) % mod + 1) % mod);
	return 0;
}

BZOJ2460

题意:选取的能量值和最大,但是序号的XOR值不能为0。

题解:贪心策略,选取能量值最大的。如果后面选取的和前面的抑或和为0,那么不选取。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1000 + 10;
int const maxbit = 64;
struct Node
{
	ll val,id;
	bool operator < (const Node& e)const{
		return val > e.val;
	}
}a[N];
int n;
ll p[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&a[i].id,&a[i].val);
	}
	sort(a+1,a+1+n);
	ll ans = 0;
	for(int i=1;i<=n;i++){
		for(int j=maxbit-1;j>=0;j--){
			if((a[i].id >> j) & 1){
				if(p[j] == 0){
					p[j] = a[i].id;
					break;
				}else{
					a[i].id ^= p[j];
				}
			}
		}
		if(a[i].id)
			ans += a[i].val;
	}
	printf("%lld\n",ans);
	return 0;
}

BZOJ2115

题意:找出从1到nXOR和最大的路劲。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const maxbit = 64;
int const N = 50000 + 10;
int const M = 100000 + 10;
struct Edge
{
	int to;
	ll val;
	Edge(int _to,ll _val):to(_to),val(_val){};
};
vector<Edge>G[N];
int n,m,cnt;
bool vis[N];
ll xorc[N<<1],p[N];   //环的个数可能大于点的个数
ll xorsum[N];  //记录从1到每一个点的XOR和
void dfs(int u,ll val){   //val表示从1到nowp的XOR和
	vis[u] = true;
	xorsum[u] = val;
	for(int i=0;i<G[u].size();i++){
		Edge &e = G[u][i]; 
		if(vis[e.to]){  //如果这个点之前来过,说明存在环,记录环的大小XOR和
			xorc[++cnt] = val ^ e.val ^ xorsum[e.to];;
		}else{
			xorsum[e.to] = val ^ e.val;
			dfs(e.to,xorsum[e.to]);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		ll val;
		scanf("%d%d%lld",&u,&v,&val);
		G[u].push_back(Edge(v,val));
	}
	dfs(1,0);
	for(int i=1;i<=cnt;i++){  //找出线性无关的基
		for(int j=maxbit-1;j>=0;j--){
			if((xorc[i] >> j) & 1){
				if(p[j] == 0){
					p[j] = xorc[i];
					break;
				}else{
					xorc[i] ^= p[j];
				}
			}
		}
	}
	ll ans = xorsum[n];
	for(int i=maxbit-1;i>=0;i--){
		if(p[i])
			ans = max(ans,ans ^ p[i]);
	}
	printf("%lld\n",ans);
	return 0;
}

CF448C

题意:有多少个集合,他们的乘积是完全平方数

题解:每个素数表示二进制的一位。完全平方数满足每个质数出现的次数为1,所以XOR后的结果为0,那么就满足完全平方数。所以结果为2^(n-r) - 1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1e5 + 10;
int const maxbit = 20;
int const mod = 1e9 + 7;
int const prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
int a[N],p[maxbit];
int n;
void factor(int n,int id){
	for(int i=0;i<19;i++){
		int now = 0;
		while(n % prime[i] == 0){
			now ^= 1;
			n /= prime[i];
		}
		a[id] |= now * (1<<i);
	}
}
ll qpow(ll a,ll n){
	ll ans = 1;
	while(n){
		if(n&1)	ans = ans * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		factor(x,i);
	}
	int r = 0;
	for(int i=1;i<=n;i++){
		for(int j=maxbit-1;j>=0;j--){
			if((a[i] >> j) & 1){
				if(p[j] == 0){
					p[j] = a[i];
					r++;
					break;
				}else{
					a[i] ^= p[j];
				}
			}
		}
	}
	printf("%lld\n",(qpow(1ll*2,n-r) - 1 + mod) % mod);
	return 0;
}

CF1100F

题意:对于每一次询问,求区间最大抑或值

题解:离线查找。记录以i为右端点的所有询问。贪心的寻找基,如果找的一个基可以取代旧的基,其坐标比原先的大,那么就选择,交换选择现在的基。因为越靠近右端点,它能被利用的区间越多。

代码:

#include <bits/stdc++.h>
using namespace std;
int const N = 500000 + 10;
int const maxbit = 20;
int n,Q,c[N],l[N],r[maxbit],p[maxbit],ans[N];
vector<int>q[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&c[i]);
	scanf("%d",&Q);
	for(int i=1;i<=Q;i++){
		int r;
		scanf("%d%d",&l[i],&r);
		q[r].push_back(i);
	}
	for(int i=1;i<=n;i++){
		int x = c[i];
		int k = i;
		for(int j=maxbit-1;j>=0;j--){
			if((x >> j) & 1){
				if(!r[j]){
					r[j] = x;
					p[j] = k;
					break;
				}else if(p[j] < k){
					swap(r[j],x);
					swap(p[j],k);
				}
				x ^= r[j];
			}
		}
		for(int j=0;j<q[i].size();j++){
			int t = q[i][j];
			for(int k=maxbit-1;k>=0;k--)	if(p[k] >= l[t]){
				ans[t] = max(ans[t],ans[t] ^ r[k]);
			}
		}
	}
	for(int i=1;i<=Q;i++)
		printf("%d\n",ans[i]);
	return 0;
}

牛客2019多校第四场B

题意:给定n个线性基,判断给定一个区间的所有线性基是否都能够产生x。

题解:线性交模板

代码

#include <bits/stdc++.h>
using namespace std;
#define lson id<<1
#define rson id<<1|1
typedef long long  ll;
int const maxbit = 32;
int const N = 50000 + 10;
int const M = 40;
ll a[N][M],x;
int n,m;
struct Base
{
    ll r[maxbit],f[maxbit];
    void init(){
        for(int i=0;i<maxbit;i++)
            r[i] = f[i] = 0;
    }
    bool insert(ll x){
        for(int i=maxbit-1;i>=0;i--) if(x >> i){
            if(!r[i]){  r[i] = x;   return true;}
            x ^= r[i];
            if(!x)  return false;
        }
        return false;
    }
    void insertn(ll x){  //插入新的基
        ll tmp = x;
        for(int i=maxbit-1;i>=0;i--) if(x >> i){
            if(!r[i]){  f[i] = tmp; r[i] = x;   return;}
            x ^= r[i];tmp ^= f[i];
            if(!x)  return;
        }
        return;
    }
    bool find(ll x){
        for(int i=maxbit-1;i>=0;i--) if(x >> i){
            if(!r[i])   return false;
            x ^= r[i];
        }
        return x == 0;
    }
    ll cacl(ll x){   //找到x是由基B中的哪几个基组成的
        ll ret = 0;
        for(int i=maxbit-1;i>=0;i--) if(x >> i){
            ret ^= f[i];  //x是由新基种类的
            x ^= r[i];
        }
        return ret;
    }
};
struct Node
{
    int l,r;
    Base base;
}node[N<<2];
void push_up(int id){
    Base base = node[lson].base;
    Base ans;
    ans.init();
    for(int i=31;i>=0;i--){
        ll x = node[rson].base.r[i];
        if(base.find(x))  ans.insert(x ^ base.cacl(x));     //如果在A能够表示,消除B的影响插入
        else    base.insertn(x);    //如果A不能够表示,直接插入
    }
    node[id].base = ans;
}
void build(int id,int l,int r){
    node[id].l = l, node[id].r = r;
    if(l == r){
        for(int i=0;i<=31;i++)
        node[id].base.insert(a[l][i]);
    }else{
        int mid = (l + r) >> 1;
        build(lson,l,mid);
        build(rson,mid+1,r);
        push_up(id);
    }
}
bool Query(int id,int l,int r,ll x){
    if(node[id].l == l && node[id].r == r) {
        return node[id].base.find(x);
    }
    int mid = (node[id].l + node[id].r) >> 1;
    if(r <= mid) return Query(lson, l, r, x);
    else if(l > mid) return Query(rson, l, r, x);
    else {
        return Query(lson, l, mid, x) && Query(rson, mid + 1, r, x);
    }
 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int sz;
        scanf("%d",&sz);
        for(int j=0;j<sz;j++)   scanf("%lld",&a[i][j]);   //表示第i组数的第j个基
        for(int j=sz;j<=31;j++)    a[i][j] = 0;
    }
    build(1,1,n);
    while(m--){
        int l,r;
        scanf("%d%d%lld",&l,&r,&x);
        if(Query(1,l,r,x))  printf("YES\n");
        else    printf("NO\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值