CF1100F - Ivan and Burgers 题解

什么是前缀线性基,爬罢

首先有个显然的线段树和 st 表维护线性基的做法。前者是预处理 2log 询问 3log,后者是预处理 3log 询问 2log 都过不了。它明显 2log 都不想让你过了。考虑发明一个 1log 的做法。

考虑把询问离线下来,按照 \(r\) 分类,扫描线,时刻维护对每个 \(l\)\([l,r]\) 线性基。

线性基有个跟 gcd 很相似的概念:从一处往右扩展,相等段的数量是 log 的。原理也跟 gcd 差不多,一开始线性基大小为 \(0\),往右移一格就可能加一或者大小不变。大小不变说明没插进去,那就整个都没变了。

那就时刻维护对当前 \(r\) 的关于 \(l\) 的 log 个线性基相等段。考虑 \(r\) 右移一位的更新?新加一个 \(([r,r],\varnothing)\) 相等段,然后把 \(a_r\) 插进前面 log 个相等段。我们需要合并新的大小相同的线性基的相等段,这样才能保证以后一直都是 log 段。但是要知道一个集合的线性基(仅化为阶梯型)与插入顺序有关。而上述操作显然都是从左往右插入的,而只有从右往左插入才能保证大小相同的线性基相同。但其实一点关系都没有,因为生成空间相等……根据的是一个(有限维)线性空间的等维子空间只有本身。那就随便选一个线性基就好啦。

那这样是 2log 的,瓶颈在于每次都要试图插入 log 次。其实对固定的 \(r\),插入的成功性是单调的,必定是连续段序列的一个后缀。那如果到第一个失败就 break,那其实成功插入的复杂度可以由势能支付掉,因为每个位置最多插入成功(大小增加)log 次,那么总共 2log,一次插入 log,总复杂度就是 3log,前面不乘以 \(n\)​ 相当于常数。而每个位置只会失败一次,所以总复杂度 1log。这样一来合并也不用写的那么精细,因为合并次数也能由势能支付掉,随便暴力枚举可合并相邻对就行了 vectorerase 也随便用,不用管那么多。

(但是好像跑得很慢,我好菜啊)

code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define X first
#define Y second
const int N=500010;
int n,qu;
int a[N];
vector<pair<int,int> > qry[N];
struct base{
	int sz,b[30];
	base(){sz=0;memset(b,0,sizeof(b));}
	void insert(int x){
		for(int i=20;~i;i--)if(x>>i&1)
			if(b[i])x^=b[i];
			else{sz++,b[i]=x;break;}
	}
	int ask(){
		int res=0;
		for(int i=20;~i;i--)if(!(res>>i&1))res^=b[i];
		return res;
	}
};
vector<pair<pair<int,int>,base> > v;
int ans[N];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	cin>>qu;
	for(int i=1;i<=qu;i++){
		int l,r;
		scanf("%d%d",&l,&r);
		qry[r].pb(mp(l,i));
	}
	for(int i=1;i<=n;i++){
		v.pb(mp(mp(i,i),base()));
		for(int j=v.size()-1;~j;j--){
			int sz=v[j].Y.sz;
			v[j].Y.insert(a[i]);
			if(v[j].Y.sz==sz)break;
		}
		while(true){
			bool flg=false;
			for(int j=0;j+1<v.size();j++)if(v[j].Y.sz==v[j+1].Y.sz){
				flg=true;
				int r=v[j+1].X.Y;
				v.erase(v.begin()+j+1);
				v[j].X.Y=r;
				break;
			}
			if(!flg)break;
		}
		for(int j=0;j<qry[i].size();j++)for(int k=0;k<v.size();k++)
			if(v[k].X.X<=qry[i][j].X&&qry[i][j].X<=v[k].X.Y)ans[qry[i][j].Y]=v[k].Y.ask();
	}
	for(int i=1;i<=qu;i++)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2021-08-12 21:59  ycx060617  阅读(61)  评论(0编辑  收藏  举报