zoukankan      html  css  js  c++  java
  • 压位优化

    很多题目都可以使用压位优化,这里介绍一下压位优化的例子。
    压位可以用来优化一些问题。通常可以把时间复杂度/=32。
    先举例子:图的传递闭包。
    先tarjan缩点,变成dag(有向无环图)。
    缩点以后每一个点都能到达强联通分量内的点。
    考虑dp。设(f_x)(x)能够到达的点。
    (f_x|=f_v),其中(v)(x)的出边。
    使用线性rmq那样的优化时间复杂度还可以/=(log_2n)
    按照拓扑序逆序构造答案。
    把点分成大小为(B)的块,有(frac{n}{B})个块。
    处理每个块(2^B)个出边的集合。
    每次遍历(frac{n}{B}),把这些块预处理的结果并在一起即可。
    这样子时间复杂度/=(log_2n)
    这样子可以用于优化uoj76,获得满分。
    还有bitset优化kosaraju。
    由于在tarjan内,每个点会被遍历多次,所以不能用tarjan。
    kosaraju的两次dfs都可以使用bitset优化。
    设标记数组的bitset为vis,每一个点(x)连出的边集为(e_x)
    则每次遍历(e_x)的时候,遍历的是(e_x)&(vis),这样子每个点就只会被遍历一次,时间复杂度/=32;
    jzoj内有一道题要用到这个技巧。
    那道题如果要修改,莫队一下。
    求出图需要(nsqrt{n})时间,每次重新缩点即可。
    再举例子namomo 1012无向图。
    题目中要求两条不相交路径,使得其距离之和最小,显然可以费用流。
    如果现在的节点是(x,y),每条边的边权和容量都是(1),则答案就是(x,y)增广(2)次的费用
    第一次bfs找最短路,第二次由于有边变负,不能bfs了。
    有一个技巧是dij费用流。先跑一遍spfa求出某个节点(任选)到所有节点的距离(d_i),然后把每条边(x,y)的权值变为(d_x-d_y+w(x,y))
    跑完费用流后,把每个点的(d)加上dij跑出来的距离的对应值。
    (d_x-d_y+w(x,y)>=0),移项得到(d_x+w(x,y)>=d_y)
    如果(d_x+w(x,y)<d_y),则(y)的权值还能再被更新。
    所以某点对(s->t)的费用是(d_s-d_t+s->t),最短路变了,但是最短路径不变。符合要求。
    但是跑完以后可能会加入反向边。
    对于每条最短路上的边,显然:
    (d'_x+w'(x,y)=d'_y)
    设势能数组为(h)
    ((d'_x+h_x)-(d'_y+h_y)+w(x,y)=0)
    ((d'_x+h_x)-(d'_y+h_y)=w(y,x))
    只需要把每个点的权值+=(d_i)即可。
    所以每条边的权值都是(>=0)的,可以跑dij。
    这样子可以再(O(n^2log_2n))内计算固定端点的答案,时间复杂度(O(n^4log_2n))
    下面叙述如何把时间复杂度优化到(frac{n^4}{omega})
    由于重新赋值后边的权值只能是(0,1,2),十分特殊,可以使用bitset优化。
    使用(3)个bitset(e_{i,0},e_{i,1},e_{i,2})维护点i权值为(0/1/2)的出边。
    如果bitset的(j)(1),则存在(i,j)这条边。
    (q_i)表示离起点距离为(<=i)的点集。
    我们要求(q_{0...n*2})
    由定义,(q_{i+1})必须要包含(q_i)
    枚举每个(i),由(q_i)更新后面的点。
    枚举权值(1,2),假设现在边权是(w),则(q_{i})可以更新(q_{i+w}),把(q_{i+w})并上(q_{i})
    这相当于批量进行dij的更新操作。
    为了节省时间,我们只需把(q_i-q_{i-1})的所有节点的(e_{v,x})并起来。
    这样子均摊时间复杂度是(O(frac{n^2}{omega}))
    为什么?因为(q_{i-1})的节点可以通过之前的操作用来更新(q_{i},q_{i+1})
    由于(q_i)更新之前会|=(q_{i-1}),所以之前已经更新过(q_{i-1})了,不用考虑(q_{i-1})的影响。
    而且每个点只会更新一次答案。均摊时间复杂度是(O(frac{n^2}{omega}))
    由于前面只使用权值为(1,2)的边更新后面的节点,所以要对于每个(i),把所有(q_i)可以到达的,连了0权边的节点加入(q_i)
    在更新连(0)权边的节点时,使用bitset来遍历每一条出边。
    在实现时,要把(0)权边加入(q_i),再用(1,2)权值的边更新后面的节点。
    由于每一个点最多被遍历(2)次,时间复杂度是(O(2*frac{n^2}{omega}))
    为了维护(e)数组,要维护一个数组(dd)(dd_i)表示重标号以后离原点距离为(i)的点的bitset。
    枚举一个原点(x),设它的距离为(p),枚举它的距离标号-出点的距离标号。
    用这两个信息和(dd)得到(e)数组。
    被卡常的代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define N 110
    #define mo 1000000007
    int T,pw[N*2],n,m,d[N],p[N][N][N],ct[N][N],pp[N],v[N][N],d1[N],dv[N][N],qu[2000000],a,b,oo;
    bitset<N>g[N],q[N*2],e[N][3],bz,tp,bv[N],w[N];
    void bfs(int x,int t){
    	a=1;
    	b=1;
    	qu[a]=x;
    	for(int i=1;i<=n;i++)
    		pp[i]=0;
    	if(t){
    		for(int i=1;i<=n;i++)
    			d1[i]=-1;
    		d1[x]=0;
    	}
    	else{
    		for(int i=1;i<=n;i++)
    			d[i]=-1;
    		d[x]=0;
    	}
    	while(a<=b){
    		int x=qu[a];
    		a++;
    		for(int i=1;i<=n;i++)
    			if(g[x][i]){
    				if(t&&d1[i]==-1){
    					d1[i]=d1[x]+1;
    					pp[i]=x;
    					qu[++b]=i;
    				}
    				else if(!t&&d[i]==-1){
    					d[i]=d[x]+w[x][i];
    					pp[i]=x;
    					qu[++b]=i;
    				}
    			}
    	}
    }
    void bb(int x,int id){
    	a=1;
    	b=1;
    	qu[a]=x;
    	bitset<N>tl;
    	while(a<=b){
    		int x=qu[a];
    		a++;
    		tp[x]=0;
    		q[id][x]=1;
    		int lb=0;
    		tl=tp&e[x][0];
    		while(1){
    			int p=(tl)._Find_next(lb);
    			if(p>n)break;
    			qu[++b]=p;
    			lb=p;
    		}
    	}
    }
    int gt(int x,int y){
    	for(int i=0;i<=2*n;i++)
    		q[i].reset();
    	bz.reset();
    	for(int i=1;i<=n;i++)
    		bz[i]=1;
    	int ans=0,ok=0;
    	for(int i=0;i<=2*n;i++){
    		if(i)q[i]|=q[i-1];
    		else q[i][x]=1;
    		bitset<110>t;
    		if(i)t=q[i]^q[i-1];
    		else t=q[i];
    		tp=bz^q[i];
    		int lb=0;
    		while(1){
    			int p=t._Find_next(lb);
    			if(p>n)break;
    			bb(p,i);
    			lb=p;
    		}
    		if(i)t=q[i]^q[i-1];
    		else t=q[i];
    		lb=0;
    		while(1){
    			int p=t._Find_next(lb);
    			if(p>n)break;
    			if(i+1<=2*n)
    				q[i+1]|=e[p][1];
    			if(i+2<=2*n)
    				q[i+2]|=e[p][2];
    			lb=p;
    		}
    		if(q[i][y]){
    			ans=i;
    			ok=1;
    			break;
    		}
    	}
    	if(!ok)return 0;
    	return (dv[x][y]+ans+dv[x][y]+mo)%mo;
    }
    int main(){
    	pw[0]=1;
    	for(int i=1;i<N*2;i++)
    		pw[i]=1ll*pw[i-1]*233%mo;
    	scanf("%d",&T);
    	int cs=0;
    	while(T--){
    		cs++;
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++){
    			g[i].reset();
    			d[i]=0;
    			e[i][0].reset();
    			e[i][1].reset();
    			e[i][2].reset();
    		}
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				ct[i][j]=0;
    		for(int i=1;i<=m;i++){
    			int a,b;
    			scanf("%d%d",&a,&b);
    			g[a][b]=1;
    			g[b][a]=1;
    		}
    		bfs(1,1);
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				w[i][j]=1;
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				if(g[i][j])
    					e[i][w[i][j]][j]=1;
    		for(int i=1;i<=n;i++){
    			bfs(i,0);
    			for(int j=1;j<=n;j++){
    				dv[i][j]=d[j];
    				int x=j;
    				while(x){
    					p[i][j][++ct[i][j]]=x;
    					x=pp[x];
    				}
    			}
    		}
    		int ans=0;
    		for(int i=1;i<=n;i++){
    			for(int j=i+1;j<=n;j++){
    				for(int k=1;k<=n;k++)
    					bv[k].reset();
    				for(int k=1;k<=n;k++)
    					bv[dv[i][k]][k]=1;
    				for(int k=1;k<=n;k++){
    					e[i][0].reset();
    					e[i][1].reset();
    					e[i][2].reset();
    				}
    				for(int k=1;k<=n;k++)
    					for(int l=-1;l<=1;l++)
    						if(dv[i][k]+l<=n&&dv[i][k]+l>=0){
    							e[k][1-l]=g[k]&bv[dv[i][k]+l];
    							e[k][1-l][k]=0;
    						}
    				for(int k=1;k<ct[i][j];k++){
    					int x=p[i][j][k+1],y=p[i][j][k],z1=-1+dv[i][y]-dv[i][x],z2=1+dv[i][x]-dv[i][y];
    					e[y][z1][x]=1;
    					e[x][z2][y]=0;
    				}
    				int t=gt(i,j);
    				ans=(ans+1ll*t*pw[i+j]%mo)%mo;
    			}
    		}
    		printf("Case %d
    ",cs);
    		ans=ans*2%mo;
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    How To Scan QRCode For UWP (4)
    How To Crop Bitmap For UWP
    How To Scan QRCode For UWP (3)
    How To Scan QRCode For UWP (2)
    How To Scan QRCode For UWP (1)
    How to change windows applicatioin's position via Win32 API
    8 Ways to Become a Better Coder
    How to resize or create a thumbnail image from file stream on UWP
    C# winform压缩文件夹带进度条
    MS ACCESS MID函数
  • 原文地址:https://www.cnblogs.com/cszmc2004/p/13454164.html
Copyright © 2011-2022 走看看