很多题目都可以使用压位优化,这里介绍一下压位优化的例子。
压位可以用来优化一些问题。通常可以把时间复杂度/=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);
}
}