zoukankan      html  css  js  c++  java
  • 区间子集最大/最小异或和问题(线性基,树上差分)

    闲话

    有这样一个问题——一个长度为(n)的序列(a_1-a_n)(q)个询问,每次询问(l,r),选出({a_l,a_{l+1}...a_{r}})中一个子集使得子集内元素异或和最大/小。

    第一次出现在HNOI模拟赛,当时的(n,q)只有大概(3*10^4)还是(10^5)的样子。然后毫不犹豫的写了个(nlog^3n+qlog^2n)的线性基ST表过了。。。

    第二次出现在NOI模拟赛,数据范围大到了(10^6)!!!因为上一次IOI金牌爷laofu踩了标算。。。然后发现自己上次没听懂,只会写个两个(log)的分治。

    第三次出现在NOI.AC的NOIP lus模拟赛,然后发现自己还是没听懂。。。。。。

    题目

    NOIAC41 最短路

    是个巧(wu)妙(chi)的二合一,另一部分的思路来自洛谷P4151 [WC2011]最大XOR和路径

    注意到线性基的一些特性:当向其中插入若干个位长为(k)的整数后,实际上真正插入到其中的只有(k)个数。也就是说,对于一个区间([l,r]),当我们从(r)(l)依次尝试插入数的过程中,最多会产生(k)个本质不同的线性基,而且这些数在线性基内的插入行两两不同。

    我们离线处理,对于右端点都在(r)的询问区间一起考虑。我们已知([1,r])的线性基每一行被插入的数在原序列中的位置(从(r)(l)插入)。对于一个询问,我们取出线性基中所有被插入位置(ge l)的行来更新答案。

    那么如何快速从([1,r-1])的线性基变到([1,r])呢?毫无疑问我们这时会先插入(a_r),设它插入到了第(j)行。那么原来在第(j)行的数现在就不会留在这里了,会(xor a_r)后继续尝试着插入下面的行。操作过程就是:把原来的数取出来,插入当前数,继续把后面原来的数取出来。。。如是循环。

    总的复杂度变成了((n+q)k),十分优秀。

    #include<bits/stdc++.h>
    #define RG register
    #define R RG int
    #define G if(++ip==iend)fread(ip=buf,1,N,stdin)
    using namespace std;
    const int N=3e5+9,M=6e5+9;
    char buf[N],*iend=buf+N,*ip=iend-1;
    int he[N],ne[M],to[M],w[M],s[N],a[N],b[N],l[N],lb[39],at[39];
    bool vis[N];
    inline int in(){
    	G;while(*ip<'-')G;
    	R x=*ip&15;G;
    	while(*ip>'-'){x*=10;x+=*ip&15;G;}
    	return x;
    }
    inline void chkmn(R&x,R y){
    	if(x>y)x=y;
    }
    void dfs(R x){
    	vis[x]=1;
    	for(R y,i=he[x];i;i=ne[i])
    		if(!vis[y=to[i]])s[y]=s[x]^w[i],dfs(y);
    }
    int main(){
    	R n=in(),m=in(),q=in(),p=0,i,r,x,y;
    	for(i=1;i<n;++i){
    		x=in();y=in();
    		ne[++p]=he[x];to[he[x]=p]=y;
    		ne[++p]=he[y];to[he[y]=p]=x;
    		w[p]=w[p-1]=in();
    	}
    	dfs(1);
    	for(i=1;i<=m;++i)
    		a[i]=s[in()]^s[in()]^in();
    	memset(he+1,0,n<<2);
    	for(i=1;i<=q;++i){
    		b[i]=s[in()]^s[in()];
    		l[i]=in();ne[i]=he[r=in()];he[r]=i;
    	}
    	for(r=1;r<=m;++r){
    		x=a[r];p=r;
    		for(i=30;~i;--i)//更新线性基
    			if(1<<i&x){
    				if(!lb[i]){
    					lb[i]=x;at[i]=p;
    					break;
    				}
    				if(at[i]<p)
    					swap(lb[i],x),swap(at[i],p);
    				x^=lb[i];
    			}
    		for(x=he[r];x;x=ne[x])//求答案
    			for(i=30;~i;--i)
    				if(at[i]>=l[x])chkmn(b[x],b[x]^lb[i]);
    	}
    	for(i=1;i<=q;++i)
    		printf("%d
    ",b[i]);
    	return 0;
    }
    
  • 相关阅读:
    Jenkins可用环境变量列表以及环境变量的使用(Shell/Command/Maven/Ant)
    CreateJS结合Falsh工具生成Canvas动画(加密字符串的由来)
    Linux下使用mv重命名文件或者移动文件(增强版的工具为rename)
    Windows7/8/10中无法识别USB设备的问题解决
    Eclipse工程中Java Build Path中的JDK版本和Java Compiler Compiler compliance level的区别(转)
    使用Docker部署Spring boot项目
    豆瓣API
    scrapy
    elasticsearch x-pack
    Document
  • 原文地址:https://www.cnblogs.com/flashhu/p/9670045.html
Copyright © 2011-2022 走看看