• 【JZOJ5064】【GDOI2017第二轮模拟day2】友好城市 Kosarajo算法+bitset+ST表+分块


    题面

    在Byteland 一共有n 座城市,编号依次为1 到n,这些城市之间通过m 条单向公路连接。
    对于两座不同的城市a 和b,如果a 能通过这些单向道路直接或间接到达b,且b 也能如此到达a,那么它们就会被认为是一对友好城市。
    Byteland 的交通系统十分特殊,第i 天只有编号在[li, ri] 的单向公路允许通行,请写一个程序,计算每天友好城市的对数。
    注意:(a, b) 与(b, a) 没有区别。

    70

    Kosarajo算法

    这是一个区别于tarjan算法的求强连通分量的算法。

    流程

    1.在逆图上进行一次dfs,然后记录下每个点的后序编号(?)。
    e.g.

    void dfs(int v){
    	dfs(next(v));  //先往后继递归
    	st[++st[0]]=v; //再在这记录后序编号
    }
    

    2.按后序编号从大到小在原图上再进行一次dfs,所能走到的就是与这个点处于同一强连通分量的点。
    3.时间复杂度为(O(n+m))

    正文

    我们看到给出的区间的左端点和右端点都是不减的,就有边只会进出一次。
    所以我们可以用邻接矩阵维护边,然后就可以使用Kosarajo算法统计答案。
    这样的时间复杂度为(O(n^2*q)),然而这还是过不了70分。

    bitset优化

    由于边不存在权值,所以我们用bitset来存储邻接矩阵。
    然后Kosarajo算法统计答案时,也要用到bitset的位运算优化。
    于是就能把复杂度优化到(O(frac{1}{32}n^2*q))

    可能会用到的bitset函数

    .reset(),归零;
    ._Find_next(int v),查找第v为的第一个1,返回位置。
    Warning:bitset的下标是从0开始算,所以如果要从头开始找,就用._Find_next(-1)。

    100

    100分与70分的区别就是,边可能会重复加入。
    注意到,如果对于两个已有的边集(邻接矩阵),那么我们可以利用bitset来优化合并,达到(O(frac{n^2}{32}))的复杂度,是很优秀的。
    我们给(m)条边分块,共(sqrt m)块,每个块考虑使用bitset来存储邻接矩阵;
    按照一般的分块套路,我们确实可以用(O(q*(sqrt m*frac{n^2}{32}+sqrt m)))来维护邻接矩阵。
    但是仍然无法被题目苛刻的条件所接受。
    于是我们考虑对块建立ST表,那么就能把维护的时间降到(O(q*(log_{sqrt m}*frac{n^2}{32}+sqrt m)))
    就能通过本题。

    为什么不用tarjan,而用Kosarajo代替

    对于tarjan而言,他需要遍历一些已经被访问的点,所以不能使用bitset优化;
    而Kosarajo,每个点只会被遍历一次,所以能使用bitset优化。

    为什么不用线段树,而用ST表和分块代替

    线段树的空间不能被接受,是(O(m*log_m*frac{n^2}{32}))

    Code

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<bitset>
    #define ll long long
    #define fo(i,x,y) for(int i=x;i<=y;i++)
    #define fd(i,x,y) for(int i=x;i>=y;i--)
    using namespace std;
    const char* fin="friend.in";
    const char* fout="friend.out";
    const int inf=0x7fffffff;
    const int maxn=157,maxm=300007,maxk=557,maxl=10;
    int n,m,q,a[maxm][2],ks,num,st[maxn],cnt=0,pre[maxn],ans;
    bitset<maxn> b[maxk][maxl][maxn],bb[maxk][maxl][maxn],bz,p[maxn],pp[maxn];
    void make(int l,int r){
    	int k=maxl-1;
    	fo(i,1,n) p[i].reset(),pp[i].reset();
    	while (l<=r){
    		if (l+(1<<k)-1<=r){
    			fo(i,1,n) p[i]|=b[l][k][i],pp[i]|=bb[l][k][i];
    			l+=(1<<k);
    		}
    		if (k>0) k--;
    	}
    }
    void dfs(int v){
    	bz.reset(v);
    	while (1){
    		int k=(pp[v]&bz)._Find_next(0);
    		if (k>n) break;
    		dfs(k);
    	}
    	st[++st[0]]=v;
    }
    void Dfs(int v){
    	cnt++;
    	bz.reset(v);
    	while (1){
    		int k=(p[v]&bz)._Find_next(0);
    		if (k>n) break;
    		Dfs(k);
    	}
    }
    void kosarajo(){
    	ans=0;
    	bz.set();
    	st[0]=0;
    	fo(i,1,n)
    		if (bz[i]){
    			dfs(i);
    		}
    	bz.set();
    	fd(i,st[0],1){
    		if (bz[st[i]]){
    			cnt=0;
    			Dfs(st[i]);
    			ans+=cnt*(cnt-1)/2;
    		}
    	}
    }
    int main(){
    	freopen(fin,"r",stdin);
    	freopen(fout,"w",stdout);
    	scanf("%d%d%d",&n,&m,&q);
    	fo(i,1,m) scanf("%d%d",&a[i][0],&a[i][1]);
    	ks=int(sqrt(m));
    	int j=1,k=ks;
    	fo(i,1,m){
    		if (i>k){
    			k+=ks;
    			j++;
    		}
    		b[j][0][a[i][0]].set(a[i][1]);
    		bb[j][0][a[i][1]].set(a[i][0]);
    	}
    	num=j;
    	fd(i,num,1){
    		fo(j,1,maxl-1){
    			if (i+(1<<(j-1))>num) break;
    			fo(k,1,n){
    				b[i][j][k]=b[i][j-1][k]|b[i+(1<<(j-1))][j-1][k];
    				bb[i][j][k]=bb[i][j-1][k]|bb[i+(1<<(j-1))][j-1][k];
    			}
    		}
    	}
    	fo(i,1,q){
    		int l,r;
    		scanf("%d%d",&l,&r);
    		int tmp=(l-1)/ks+1,tmd=(r-1)/ks+1;
    		make(tmp+1,tmd-1);
    		if (tmp!=tmd){
    			fo(j,l,tmp*ks) p[a[j][0]].set(a[j][1]),pp[a[j][1]].set(a[j][0]);
    			fo(j,(tmd-1)*ks+1,r) p[a[j][0]].set(a[j][1]),pp[a[j][1]].set(a[j][0]);
    		}else fo(j,l,r) p[a[j][0]].set(a[j][1]),pp[a[j][1]].set(a[j][0]);
    		/*fo(i,1,n) cout<<p[i]<<endl;
    		fo(i,1,n) cout<<pp[i]<<endl;*/
    		kosarajo();
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    java 数组及数组得内存管理总结
    js 日期格式化
    url获取参数值,支持中文、英文
    C# log4net 的日志代码配置
    js 处理浏览器显示地址
    mui <a>标签跳转失效的处理
    js 实时输入事件
    asp.mvc 页面获取当前请求的控制器和方法
    js 获取元素值
    DllImport System.DllNotFoundException 找不到指定的模块。 (Exception from HRESULT: 0x8007007E)
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6727057.html
Copyright © 2020-2023  润新知