【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\n",ans);
}
return 0;
}
【JZOJ5064】【GDOI2017第二轮模拟day2】友好城市 Kosarajo算法+bitset+ST表+分块的更多相关文章
- GDOI2017第二轮模拟day1 总结
平民比赛 这场比赛的暴力分非常友好. 但是我并没有拿到全部的暴力分. 1(暴力分\(60/100\)) 暂时我可以拿的暴力分为\(30/100\),直接mst模拟即可. 然而当时打了个辣鸡莫队,结果爆 ...
- 【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树
题面 在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci. Byteasar作为Byteland 公路建设项目的总工程师,他决定选定 ...
- [jzoj5073 GDOI2017第二轮模拟] 影魔
Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵 ...
- 【JZOJ5088】【GDOI2017第四轮模拟day2】最小边权和 排序+动态规划
题面 有一张n个点m条边的有向图,每条边有一个互不相同的边权w,有q个询问,要求你从点a经过不超过c条边到点b,要求经过的边权递增并和尽量小,求出最小的边权和,如果没有合法方案则输出-1. 对于100 ...
- 【JZOJ5071】【GDSOI2017第二轮模拟】奶酪 树形dp
题面 CJY很喜欢吃奶酪,于是YJC弄到了一些奶酪,现在YJC决定和CJY分享奶酪. YJC弄到了n-1块奶酪,于是他把奶酪挂在了一棵n个结点的树上,每根树枝上挂一块奶酪,每块奶酪都有重量. YJC和 ...
- 【JZOJ5068】【GDSOI2017第二轮模拟】树 动态规划+prufer序列
题面 有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i]. 现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数. 10 ...
- 【Noip模拟 20161005】友好城市
问题描述 小ww生活在美丽的ZZ国.ZZ国是一个有nn个城市的大国,城市之间有mm条单向公路(连 接城市ii.jj的公路只能从ii连到jj).城市ii.jj是友好城市当且仅当从城市ii能到达城市jj并 ...
- DFRobot万物互联大赛第二轮
前言 最近放在阳台的花草被啥东西给吃了,然后厨房挂在墙上的小虾米也不知道咋的被抓破吃光了(我怀疑是隔隔壁两条泰迪),所以打算做个简单的项目,教训一下偷吃贼.时间比较仓促,内容比较多,能力有比较有限,好 ...
- 【百度之星2014~初赛(第二轮)解题报告】Chess
声明 笔者近期意外的发现 笔者的个人站点http://tiankonguse.com/ 的非常多文章被其他站点转载.可是转载时未声明文章来源或參考自 http://tiankonguse.com/ 站 ...
随机推荐
- VNC 4.25注册码
注册码:ELBMU-ZFYMV-2HC77-73M46-UL4TA97KLJ-VBTAL-T7GN2-K29PS-ANXCA45YV6-WXWMJ-NPAAV-HWD7Q-W5HVAL76HR-642 ...
- Apache Pig入门学习文档(一)
1,Pig的安装 (一)软件要求 (二)下载Pig (三)编译Pig 2,运行Pig (一)Pig的所有执行模式 (二)pig的交互式模式 (三)使用pig脚本 ...
- java笔试之计算n x m的棋盘格子
请编写一个函数(允许增加子函数),计算n x m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往 ...
- ubuntu与xshell连接不起来:
- HBase 概念视图
- Luogu P3007 [USACO11JAN]大陆议会The Continental Cowngress
P3007 [USACO11JAN]大陆议会The Continental Cowngress 题意 题意翻译 简述:给出\(n\)个法案,\(m\)头牛的意见,每头牛有两个表决格式为"支持 ...
- 安卓自定义View进阶-Canvas之画布操作 转载
安卓自定义View进阶-Canvas之画布操作 转载 https://www.gcssloop.com/customview/Canvas_Convert 本来想把画布操作放到后面部分的,但是发现很多 ...
- IDEA取消形参名显示
idea默认情况下如显示形参名,看起来有点不习惯 现在设置去掉 (1)点击工具栏上的快捷按钮(快捷键:alt + ctrl + s) 或者"File" -> "Se ...
- 初识类(class&struct)及C/C++封装的差异
初识类(class&struct) 面向对象三大特性:封装.继承和多态.其中不得不谈的就是类,通过类创建一个对象的过程叫实例化,实例化后使用对象可以调用类成员函数和成员变量,其中类成员函数称为 ...
- 关于UML类图的一点理解(转)
首先我们定义一个非常简单的Person类,代码如下: public class Person { private String name; private int age =1; public Str ...