BZOJ2208 [Jsoi2010]连通数[缩点/Floyd传递闭包+bitset优化]
显然并不能直接dfs,因为$m$会非常大,复杂度就是$O(mn)$;
这题有三种做法,都用到了bitset的优化。第二种算是一个意外的收获,之前没想到竟然还有这种神仙操作。。
方法一:缩点+DAG上bitset优化的统计
做有向图连通问题上来先看可不可以缩点首先一个环内点是可以相互连通的,又发现DAG也许方便统计,于是缩点。。然后变成一张DAG,只要统计每个点可以往后走到的所有点权(指该环包含的点数)的和。并不好直接把后继全加上去,因为会有点被重复统计。为了避免重复统计,只要直接每个点开一个bitset,表示他能走到哪些点,这样dp的时候直接对儿子取or就可以了,然后再统计,不会重复。复杂度$O(\frac{mn}{32})$。实际上是可以卡过去的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<bitset>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=+;
struct thxorz{
int to[N*N],nxt[N*N],head[N],tot;
inline void add(int x,int y){to[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
}G1,G2;
int from[N*N];
char s[N];
int n,m,ans;
#define y G1.to[j]
int dfn[N],low[N],tim,stk[N],instk[N],bel[N],sum[N],val[N],Top;
void tarjan(int x){
dfn[x]=low[x]=++tim,stk[++Top]=x,instk[x]=;
for(register int j=G1.head[x];j;j=G1.nxt[j]){
if(!dfn[y])tarjan(y),MIN(low[x],low[y]);
else if(instk[y])MIN(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
int tmp;
do instk[tmp=stk[Top--]]=,bel[tmp]=x,++sum[x];while(tmp^x);
}
}
#undef y
#define y G2.to[j]
bitset<N> S[N];
void dp(int x){
if(val[x])return;
S[x][x]=;
for(register int j=G2.head[x];j;j=G2.nxt[j])dp(y),S[x]|=S[y];
for(register int i=;i<=n;++i)if(S[x][i])val[x]+=sum[i];
val[x]=val[x]*sum[x];
}
#undef y
int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
read(n);
for(register int i=;i<=n;++i){
scanf("%s",s+);
for(register int j=;j<=n;++j)if(i^j&&s[j]=='')G1.add(i,j),from[G1.tot]=i;
}
for(register int i=;i<=n;++i)if(!dfn[i])tarjan(i);
for(register int t=,x,y;t<=G1.tot;++t){
x=from[t],y=G1.to[t];
if(bel[x]^bel[y])G2.add(bel[x],bel[y]);
}
for(register int i=;i<=n;++i)dp(i),ans+=val[i];
printf("%d\n",ans);
return ;
}
方法二:Floyd传递闭包+bitset优化
这个优化真是妙啊,之前学Floyd都没想过这个优化。`````
连通性可以用二元逻辑关系表示,也就是可以用Floyd来做传递闭包。具体来说,就是设$f_{i,j}=0/1$表示$i$到$j$是否可达。然后转移的话是$\text{f[i][j]|=f[i][k]&f[k][j]}$。
注意这个转移。$f_{i,j}$相当于看成对于$i$点,用一个bitset表示他到$1\sim n$各点的可达性。
再观察,$f[i][k]$值已经是定的了,如果是$0$就不转移了,是$1$的话,对于每一位$j$,只要用$f[k]$对应的$j$位or一下就可以了。并且$f[i][k]$在本次转移中是不会变的。
所以,直接用bitset整体进行or操作就行了。$O(\frac{n^3}{32})$。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<bitset>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=+;
bitset<N> f[N];
int n,ans;
char s[N]; int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
read(n);
for(register int i=;i<=n;++i){
scanf("%s",s+);
for(register int j=;j<=n;++j)f[i][j]=s[j]==''||i==j;
}
for(register int k=;k<=n;++k)
for(register int i=;i<=n;++i)
if(f[i][k])f[i]|=f[k];
for(register int i=;i<=n;++i)ans+=f[i].count();
return printf("%d\n",ans),;
}
方法三:暴力BFS+bitset标记松弛点(手写警告)
对每一个点,直接开始BFS,然后这个做法时间瓶颈在于每次都要枚举到所有边,那么我么尽量让他BFS时候从松弛点去走没有被访问过的点,避免走路径通向访问过的点的。可以用bitset维护目前所有点哪些没有被访问过,每个节点再开一个bitset表示他通往哪些点,每次BFS松弛的时候,把两者and一下,这样所有的$1$位就是没有访问过的,仅取出这些$1$,访问之即可。这样,就使得走过的边数量与点数相同,复杂度就降成了$O(n(\frac{n^2}{32}+n))$。不过,这种方法由于要取出$1$,如果直接对bitset每一位直接检验,复杂度就又回去了,而STL的bitset太垃圾没有这个取$1$复杂度和$1$数量相同的方法,所以我们要手写bitset。。。这种方法是yql讲过的,由于我太菜了,所以我不会写,实际要咕咕咕。
BZOJ2208 [Jsoi2010]连通数[缩点/Floyd传递闭包+bitset优化]的更多相关文章
- [bzoj2208][Jsoi2010]连通数_bitset_传递闭包floyd
连通数 bzoj-2208 Jsoi-2010 题目大意:给定一个n个节点的有向图,问每个节点可以到达的点的个数和. 注释:$1\le n\le 2000$. 想法:网上有好多tarjan+拓扑序dp ...
- 2018.09.11 bzoj2208: [Jsoi2010]连通数(bitset+floyd)
传送门 听说正解是缩点+dfs? 直接bitset优化floyd传递闭包就行了.(尽管时间复杂度是假的O(n3/32)" role="presentation" styl ...
- BZOJ2208: [Jsoi2010]连通数(tarjan bitset floyd)
题意 题目链接 Sol 数据水的一批,\(O(n^3)\)暴力可过 实际上只要bitset优化一下floyd复杂度就是对的了(\(O(\frac{n^3}{32})\)) 还可以缩点之后bitset维 ...
- bzoj2208 [Jsoi2010]连通数(scc+bitset)
2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1879 Solved: 778[Submit][Status ...
- [BZOJ2208]:[Jsoi2010]连通数(暴力 or bitset or 塔尖?)
题目传送门 题目描述 度量一个有向图连通情况的一个指标是连通数,指图中可达顶点对的个数. 在上图中,顶点1可以到达1.2.3.4.5. 顶点2可以到达2.3.4.5. 顶点3可以到达3.4.5. 顶点 ...
- [BZOJ2208][Jsoi2010]连通数 暴力枚举
Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图 ...
- BZOJ2208: [Jsoi2010]连通数
tarjan缩点后拓扑排序,每一个点用一个bitset记录哪些点能到达它. PS:数据太水,暴力能过. #include<bits/stdc++.h> using namespace st ...
- POJ 3275 Ranking the cows ( Floyd求解传递闭包 && Bitset优化 )
题意 : 给出 N 头牛,以及 M 个某些牛之间的大小关系,问你最少还要确定多少对牛的关系才能将所有的牛按照一定顺序排序起来 分析 : 这些给出的关系想一下就知道是满足传递性的 例如 A > B ...
- HDU 5036 Explosion (传递闭包+bitset优化)
<题目链接> 题目大意: 一个人要打开或者用炸弹砸开所有的门,每个门后面有一些钥匙,一个钥匙对应一个门,告诉每个门里面有哪些门的钥匙.如果要打开所有的门,问需要用的炸弹数量为多少. 解题分 ...
随机推荐
- shell备份脚本
#!/bin/bash #不存在的变量终止脚本执行 set -o nounset #执行出错终止脚本执行 set -o errexit #递归列出文件的绝对路径并执行压缩 delDir=`date - ...
- Oracle快速运行一指禅
对于oracle数据库下的企业级应用开发,经常会使用到新建用户,新建表空间以及数据的迁移工作.虽然目前互联网存在很多单个问题的解决方案,但是比较零散,本博文结合研发兄弟们的实际现状,提供一套完整初 ...
- linux安装java jdk
一.检查是否已经安装java jdk. 检查是否有自带的openJdk, 若有将其卸载删除.详情第四步 二.jdk下载安装. 1.创建文件夹 mkdir /usr/local/java #自选目录,一 ...
- 10.使用du将文件按大小进行排序
按G进行排序du -sh * | grep G | sort -nr
- 学习扩展kmp
参考博客:https://blog.csdn.net/s_999999/article/details/89104957
- 如何设置输入IP地址就直接访问到某一个网站
如何设置输入IP地址就直接访问到某一个网站 1).在IIS中添加好站点后,在网站绑定中设置明确的IP地址,如下图: 2).修改Default WebSite的端口,或者是把Default WebSit ...
- extra bytes at beginning or within zipfile
主要用文本文档打开看看是否带有#!/bin/bash 修改pom文件<executable>false</executable>
- Java并发与多线程教程(1)
Java并发性与多线程介绍 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务 ...
- Python实现乘法表——在列表里进行for循环设初值
代码:最大的收获是二维列表的实现:[0]*9结果是[0,0,0,0,0,0,0,0,0,0],再加上for i in range(9),代表是9行[0,0,0,0,0,0,0,0,0,0],也就是9* ...
- C++ STL 之 queue
queue 是一种先进先出(first in first out, FIFO)的数据类型,他有两个口,数据元素只能从一个口进,从另一个口出.队列只允许从队尾加入元素,队头删除元素,必须符合先进先出的原 ...