题面

在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. 2019-2-18-VisualStudio-给项目添加特殊的-Nuget-的链接

    title author date CreateTime categories VisualStudio 给项目添加特殊的 Nuget 的链接 lindexi 2019-02-18 15:56:48 ...

  2. create-react-app 创建项目失败

    创建失败后查阅相关资料,亲测删除 C:\Users\Administrator\AppData\Roaming\npm-cache\ 该文件夹下所有内容后成功.

  3. Mysql8+mybatisGenerator (mysql 8的逆向工程)

    最近试了一下mysql8的逆向工程工具 1.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOC ...

  4. 图像复原MATLAB实现

    前言:本篇博客先介绍滤波器滤除噪声,再介绍滤波器复原,侧重于程序的实现. 一:三种常见的噪声 二:空间域滤波 空间域滤波复原是在已知噪声模型的基础上,对噪声的空间域进行滤波.空间域滤波复原方法主要包括 ...

  5. 关于解码芯片SAA7115及7105的配置 ,FVID与微驱动

    最近因为在做视频处理,所以和解码芯片打交道很多,根据DM642的例程总了一些应该配置的东西. 注:以下的配置均是指AV信号输入,PAL制式输出的条件 (一)关于SAA7115(基于TI的驱动) 结构体 ...

  6. Python玩转人工智能最火框架 TensorFlow应用实践

    Python玩转人工智能最火框架 TensorFlow应用实践 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课 ...

  7. JavaScript对象继承方式

    一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Parent 构造函数 成为 Children 的方法, ...

  8. JasperReports报表变量13

    报表变量是建立在报表表达式之上的特殊对象.报表变量简化以下任务: 报表,其中大量使用在整个报告模板表达式.这些表达式可以通过使用报表变量只能声明一次. 计数,求和,平均,最低,最高,方差等:报表变量可 ...

  9. MFC 双缓存绘图

    在SDI应用程序中,当我们需要时刻动态刷新界面的时候,如果我们一直使用,UpdateAllView()那么就会出现屏幕不停闪烁.闪屏非常严重,特别是一直在动态刷新的时候.并且在闪屏的过程中 我们根本就 ...

  10. Python模块安装方式

    一.方法1: 单文件模块直接把文件拷贝到 $python_dir/lib/python3.4/site-packages/ 二.方法2: 多文件模块,带setup.py 下载模块包,进行解压,进入模块 ...