前缀线性基——关于目前的理解以及一些样题

 怎么说呢?在前几天我总结了了有关线性基的一篇博客,线性基用来去求整个区间的异或最值问题

前缀线性基——用于统计一个区间内的异或最值问题

 那么我们如何去统计呢?那么就要去存储一个区间的异或空间线性基,因此我们的思路就是用base[ p ] [ i ] 表示第p版本(统计从下标1~p上元素的线性基)的第i为上存储的线性基是什么

用pos[ p ] [ i ]去表示第p版本上的第i位上的线性基其元素来自于哪个下标的数

那么我们如何去查询呢?

我们去看当前右区间这个版本的线性基的位置,如果当前第i位上的线性基如果小于L,那么就说明此处线性基不存在于这个区间中,不用算这个线性基

然后就是正常求最值了

直接看样例

例题

F. Ivan and Burgers

其实这题就是一道完完全全的前缀线性基板题, 就是说给你n个数,然后q个询问,然后每次给你一个边界L和R,然后问你这个区间里面的异或最大值是多少

我们之间用前缀线性基去解决就行,具体注释我添加在代码里面,不懂私信即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int x;
int base[500005][32];//表示从1~i的版本上的第j位线性基的值
int pos[500005][32];//表示从1~i的版本上的第j位线性基的位置
int l,r;

void add(int x,int id)
{
	for(int i=0;i<32;i++)
	{
		base[id][i]=base[id-1][i];//子承父
		pos[id][i]=pos[id-1][i];
	}
	int p=id;//记录当前的版本号方便操作
	for(int i=31;i>=0;i--)
	{
		if((x>>i)&1)
		{
			if(!base[id][i])//如果当前位线性基是空的,可以直接插入
			{
				base[id][i]=x;
				pos[id][i]=p;//记录当前位线性基的位置
				break;
			}
			else
			{
				if(pos[id][i]<p)//如果当前位的线性基不是空的,要更新当前线性基的值向右方拓展
				{
					swap(base[id][i],x);//交换线性基的值
					swap(pos[id][i],p);//位置也会相应发生改变
				}
				x^=base[id][i];
			}
		}
	}
}

int find(int l,int r)
{
	int ans=0;
	for(int i=31;i>=0;i--)
	{
		if(pos[r][i]<l)
		{
			continue;
		}
		ans=max(ans,ans^base[r][i]);
	}
	return ans;
}

signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>x;
		add(x,i);
	}
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		cin>>l>>r;
		cout<<find(l,r)<<"\n";
	}
	return 0;
}

P3292 [SCOI2016] 幸运数字

 题解:其实也是一道前缀线性基的问题,只不过还需要用到LCA+线性基合并

我们可以将一条路径上的链拆分为两条链,假设lca是x和y点的最近公共祖先,那么我们就可以将这条路径拆分为x~lca链和y~lca链,然后,我们就先将x链的线性基插入到线性空间中,然后将y链的线性基合并到线性空间中即可解决问题

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int a[20005];
int u,v;
vector<int> e[20005];
int dep[20005];
int f[20005][16];
int base[20005][64];
int pos[20005][64];
int b[20005];
void add(int v,int fa)
{
    for(int i=0; i<=63; i++)
    {
        base[v][i]=base[fa][i];
        pos[v][i]=pos[fa][i];
    }
    int x=a[v];
    int p=v;
    for(int i=63; i>=0; i--)
    {
        if((x>>i)&1)
        {
            if(!base[v][i])
            {
                base[v][i]=x;
                pos[v][i]=p;
                break;
            }
            else
            {
                if(dep[pos[v][i]]<dep[p])
                {
                    swap(base[v][i],x);
                    swap(pos[v][i],p);
                }
                x^=base[v][i];
            }
        }
    }
}

void dfs(int v,int fa)
{
    dep[v]=dep[fa]+1;
    f[v][0]=fa;
    for(int i=1; i<16; i++)
    {
        f[v][i]=f[f[v][i-1]][i-1];
    }
    add(v,fa);
    for(int u:e[v])
    {
        if(u!=fa)
        {
            dfs(u,v);
        }
    }
}

int LCA(int u,int v)
{
    if(dep[u]<dep[v])
    {
        swap(u,v);
    }
    for(int i=15; i>=0; i--)
    {
        if(dep[f[u][i]]>=dep[v])
        {
            u=f[u][i];
        }
    }
    if(u==v)
        return u;
    for(int i=15; i>=0; i--)
    {
        if(f[u][i]!=f[v][i])
        {
            u=f[u][i];
            v=f[v][i];
        }
    }
    return f[u][0];
}

int find(int x,int y)
{
    int lca=LCA(x,y);
    for(int i=63; i>=0; i--)
    {
        if(dep[pos[x][i]]>=dep[lca])
        {
            b[i]=base[x][i];
        }
        else
        {
            b[i]=0;
        }
    }
    for(int i=63; i>=0; i--)
    {
        if(dep[pos[y][i]]>=dep[lca])
        {
            int z=base[y][i];
            for(int j=i; j>=0; j--)
            {
                if((z>>j)&1)
                {
                    if(!b[j])
                    {
                        b[j]=z;
                        break;
                    }
                    else
                    {
                        z^=b[j];
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=63; i>=0; i--)
    {
        ans=max(ans,ans^b[i]);
    }
    return ans;
}

signed main()
{
    cin>>n>>q;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    for(int i=1; i<=n-1; i++)
    {
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1; i<=q; i++)
    {
        cin>>u>>v;
        cout<<find(u,v)<<"\n";
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值