题面

在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. HtmlHelper2

    一.隐式从ViewBag取数据 1.action中的代码: ViewBag.UserName = "admin"; cshtml中的代码: @Html.TextBox(" ...

  2. ElasticSearch入门之彼行我释(四)

    散仙在上篇文章中,介绍了关于ElasticSearch基本的增删改查的基本粒子,本篇呢,我们来学下稍微高级一点的知识: (1)如何在ElasticSearch中批量提交索引 ? (2)如何使用高级查询 ...

  3. 2018-10-31-win10-uwp-使用-asp-dotnet-core-做图床服务器客户端

    title author date CreateTime categories win10 uwp 使用 asp dotnet core 做图床服务器客户端 lindexi 2018-10-31 14 ...

  4. Android 开发 AudioRecord音频录制

    前言 Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压 ...

  5. layui 表格点击图片放大

    表格 ,cols: [[ //表头 {checkbox: true,fixed: true} ,{type: 'numbers', title: 'ID', sort: true,width:80} ...

  6. python 解决抓取网页中的中文显示乱码问题

    关于爬虫乱码有很多各式各样的问题,这里不仅是中文乱码,编码转换.还包括一些如日文.韩文 .俄文.藏文之类的乱码处理,因为解决方式是一致的,故在此统一说明. 网络爬虫出现乱码的原因 源网页编码和爬取下来 ...

  7. NOIP2017普及组初赛总结

    去年,我普及组复赛翻车,居然没进一等奖,于是,今年,我只能再做一次普及组. 这次初赛我93.5分,居然是中山市第一--(中山市太弱了?) 其实我觉得我没考好. 比赛时第二题(计算机存储数据的基本单位是 ...

  8. 你所不知道的Mac截图的强大

    Mac的截图功能扩展功能很强大的,不要用QQ那个COM+Ctrl+A弱爆了的截图了~ 首先说一下两种截图 1.Command+shift+3:全屏截图,保存截图到桌面 2.Command+shift+ ...

  9. PipeCAD 简介

    PipeCAD 简介 PipeCAD的定位是中小型项目的管道设计软件,主要有管道建模.设备建模以及管道ISO图及平面图功能.程序的操作方式尽量参考PDMS,考虑灵活性.易于使用.如果用来和国内其他管道 ...

  10. 群晖的moments套件 发生未知错误

    一次了,哎 也不知道什么原因引起的 只能再搞一遍 先把homes文件夹数据弄走,免得弄丢 然后卸载momemts,删除数据库 还有其他人遇到这个情况http://www.gebi1.com/threa ...