题面

在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\n",ans);
}
return 0;
}

【JZOJ5064】【GDOI2017第二轮模拟day2】友好城市 Kosarajo算法+bitset+ST表+分块的更多相关文章

  1. GDOI2017第二轮模拟day1 总结

    平民比赛 这场比赛的暴力分非常友好. 但是我并没有拿到全部的暴力分. 1(暴力分\(60/100\)) 暂时我可以拿的暴力分为\(30/100\),直接mst模拟即可. 然而当时打了个辣鸡莫队,结果爆 ...

  2. 【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树

    题面 在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci. Byteasar作为Byteland 公路建设项目的总工程师,他决定选定 ...

  3. [jzoj5073 GDOI2017第二轮模拟] 影魔

    Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵 ...

  4. 【JZOJ5088】【GDOI2017第四轮模拟day2】最小边权和 排序+动态规划

    题面 有一张n个点m条边的有向图,每条边有一个互不相同的边权w,有q个询问,要求你从点a经过不超过c条边到点b,要求经过的边权递增并和尽量小,求出最小的边权和,如果没有合法方案则输出-1. 对于100 ...

  5. 【JZOJ5071】【GDSOI2017第二轮模拟】奶酪 树形dp

    题面 CJY很喜欢吃奶酪,于是YJC弄到了一些奶酪,现在YJC决定和CJY分享奶酪. YJC弄到了n-1块奶酪,于是他把奶酪挂在了一棵n个结点的树上,每根树枝上挂一块奶酪,每块奶酪都有重量. YJC和 ...

  6. 【JZOJ5068】【GDSOI2017第二轮模拟】树 动态规划+prufer序列

    题面 有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i]. 现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数. 10 ...

  7. 【Noip模拟 20161005】友好城市

    问题描述 小ww生活在美丽的ZZ国.ZZ国是一个有nn个城市的大国,城市之间有mm条单向公路(连 接城市ii.jj的公路只能从ii连到jj).城市ii.jj是友好城市当且仅当从城市ii能到达城市jj并 ...

  8. DFRobot万物互联大赛第二轮

    前言 最近放在阳台的花草被啥东西给吃了,然后厨房挂在墙上的小虾米也不知道咋的被抓破吃光了(我怀疑是隔隔壁两条泰迪),所以打算做个简单的项目,教训一下偷吃贼.时间比较仓促,内容比较多,能力有比较有限,好 ...

  9. 【百度之星2014~初赛(第二轮)解题报告】Chess

    声明 笔者近期意外的发现 笔者的个人站点http://tiankonguse.com/ 的非常多文章被其他站点转载.可是转载时未声明文章来源或參考自 http://tiankonguse.com/ 站 ...

随机推荐

  1. springboot2 +thymeleaf

    springboot 1.5. 9+ thymeleaf <!--sidebar--> <nav class="col-md-2 d-none d-md-block bg- ...

  2. Netty TCP粘包/拆包问题《二》

    1.DelimiterBasedFrameDecoder:是以分隔符作为结束标志进行解决粘包/拆包问题 代码: EchoClient:客户端 /* * Copyright 2012 The Netty ...

  3. Linux-c给线程取名字

    https://blog.csdn.net/jasonchen_gbd/article/details/51308638 #define wtm_set_thread_name(n) ({ \ ] = ...

  4. Gym100889L

    Gym100889Lhttps://vjudge.net/problem/341988/origin题目大意:有一个n*n的图,m条双向边(没有重边自环),求从每个节点出发走k条路后到其他所有节点的最 ...

  5. 初识css3 3d动画效果

    (先看我博客右上角的3d盒子动画效果,目前没做兼容处理,最好最新的chrome看)无意间看到网上css3写的3d动画效果,实在炫酷,以前理解为需要js去计算去写,没想到css直接可以实现.于是开始研究 ...

  6. c++容器的操作方法总结

    一.map 1.创建 typedef map<int,string> descrbe_map_; descrbe_map_ devMap; 或者 map<string,string& ...

  7. org.apache.ibatis.executor.ExecutorException: No constructor found in xxxClass matching [java.lang.String, java.lang.Long, java.lang.String, java.lang.String, java.sql.Timestamp]

    解决方案 为bean加上默认构造方法 public class User { private String username; private long id; private String sex; ...

  8. 写js过程中遇到的一个bug

    <div class="func_Div" id="xxcx"><span>信息查询</span>              ...

  9. 装配SpringBean(四)--注解装配之组件扫描

    前两篇文章我总结了通过XML方式装配bean的实现方式,虽然比较简单,但是需要配置很多,很多时候我们都会使用注解进行装配.使用注解的方式可以减少XML的配置,既能实现XML的功能,还提供了自动装配功能 ...

  10. 观察者模式(Observer、Subject、ConcreteSubject、ConcreteObserver)(监护、订阅)

    建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应. 在此发生改变的对象称之为观察目标(被观察者),而被通知的对象称为观察者,一个观察者目标可以对应多个观 ...