zoukankan      html  css  js  c++  java
  • CF1100F

    什么是前缀线性基,爬罢

    首先有个显然的线段树和 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
    ",ans[i]);
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    Android 在Java代码中设置style属性使用代码创建ProgressBar对象
    编写高效的Android代码(提高运行速度,节省电量)
    Android动态加载——加载已安装APK中的类
    使用NDK移植开源项目,JNI的使用技巧
    [随便写写]Android基础教程
    Android 通过按键旋转屏幕
    struts2中获取request、response,与android客户端进行交互
    Oracle配置监听和连接,已经一些比较容易混淆的相关概念
    Oracle Profile 使用详解
    VS.NET(VC++风格)常用快捷键 && UltraEdit常用快捷键
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-cf1100f.html
Copyright © 2011-2022 走看看