ZROI#1013
ZROI#1013
看起来以为是个 \(Floyd\) 传递闭包...
一看数据范围,算了叭.这拿头过啊.
一拿过来肯定缩点嘛.
缩完点成为一个 \(DAG\) , 一般 \(DAG\) 先考虑考虑拓扑排序后再做.
那么这个题怎么考虑拓扑呢?暂时还没有思路.
但我们发现,如果我们有一条边 \((u,v)\) , 那么 \(v\) 能达到的点, \(u\) 也一定能到达,因为 \(u\) 能到达 \(v\).
如果我们把每个点能到的点视为一个二进制状态,那么显然有 \(status_u = status_u | status_v\)
那么这是显然可以一路递推的,怎么递推呢?逆拓扑!
总体思路呼之欲出了 \(:\)
先缩点成为一个 \(DAG\) , 再对 \(DAG\) 做拓扑,以逆拓扑的顺序递推得到每个点的状态,查询直接查询对应位即可.
但我们发现这样很不行,因为共有 \(2\times 10^5\) 个点,每个点都要开这么大二进制状态,开出来人都没了.
不如你每 \(64\) 个点压一个 \(unsigned \: long \: long\) 然后每个点只需要至多 \(\cfrac{n}{64}\) 个 \(ull\) 就能存下,这样的空间就可以负担的起了.
那么怎么实现呢?一个简单的方法是指针实现,开一个长度为 \(1e8\) 的内存池.
对每个点及其所需空间顺次放入,用一个 \(2\times 10^5\) 的指针数组指向每个点的起始位置,然后空出 \(\cfrac{n}{64}\) 个位置留作第二维.
这样,我们要在点 \(u\) 的状态中找一个点 \(v\) , 只需要把 \(v\) 对应的编号除以 \(64\) 得到它是在 \(u\) 之后的第几个位置,然后再和对应的位数取 \(\&\) 即可.
\(Code:\)
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define MEM(x,y) memset ( x , y , sizeof ( x ) )
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define pii pair < int , int >
#define one first
#define two second
#define rint read<int>
#define pb push_back
#define db double
#define ull unsigned long long
#define lowbit(x) ( x & ( - x ) )
using std::queue ;
using std::set ;
using std::pair ;
using std::max ;
using std::min ;
using std::priority_queue ;
using std::vector ;
using std::swap ;
using std::sort ;
using std::unique ;
using std::greater ;
struct ios {
inline char gc(){
static const int IN_LEN=1<<18|1;
static char buf[IN_LEN],*s,*t;
return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
}
template <typename _Tp> inline ios & operator >> (_Tp&x){
static char ch,sgn; ch = gc(), sgn = 0;
for(;!isdigit(ch);ch=gc()){if(ch==-1)return *this;sgn|=ch=='-';}
for(x=0;isdigit(ch);ch=gc())x=x*10+(ch^'0');
sgn&&(x=-x); return *this;
}
} io;
const int N = 2e5 + 100 ;
const int M = 1e8 + 100 ;
const int E = 1e6 + 100 ;
struct edge { int to , next ; } e[E] , G[E] ;
queue < int > q ;
int n , m , qs , ind[N] , dfn[N] , low[N] , top , L , head1[N] ;
int idx[N] , siz[N] , cnt , tot , s[N] , tot1 , tot2 , head2[N] ;
ull *now , *bit[N] , pool[M] ; bool ins[N] ;
inline void tarjan (int cur) {
s[++top] = cur ; ins[cur] = true ;
dfn[cur] = low[cur] = ++ cnt ;
for (int i = head1[cur] ; i ; i = e[i].next) {
int k = e[i].to ;
if ( ! dfn[k] ) {
tarjan ( k ) ;
low[cur] = std::min ( low[cur] , low[k] ) ;
} else if ( ins[k] ) low[cur] = std::min ( low[cur] , dfn[k] ) ;
}
if ( low[cur] == dfn[cur] ) {
while ( s[top+1] != cur ) {
idx[s[top]] = tot ;
++ siz[tot] ;
ins[s[top--]] = false ;
}
++ tot ;
}
return ;
}
inline void update (int x , int y) { rep ( i , 0 , L - 1 ) bit[y][i] |= bit[x][i] ; return ; }
inline void build (int u , int v ) {
e[++tot1].next = head1[u] ;
e[tot1].to = v ; head1[u] = tot1 ;
}
inline void _build (int u , int v ) {
G[++tot2].next = head2[u] ;
G[tot2].to = v ; head2[u] = tot2 ;
}
signed main (int argc , char * argv[]) {
io >> n >> m >> qs ;
rep ( i , 1 , m ) { int u , v ; io >> u >> v ; build ( u , v ) ; }
rep ( i , 1 , n ) if ( ! dfn[i] ) tarjan ( i ) ;
rep ( i , 1 , n ) for (int j = head1[i] ; j ; j = e[j].next) {
int k = e[j].to ;
if ( idx[i] != idx[k] ) { _build ( idx[k] , idx[i] ) ; ++ ind[idx[i]] ; }
}
now = pool ; L = ( tot + 63 ) / 64 ;
rep ( i , 0 , tot - 1 ) {
bit[i] = now ; now += L ;
bit[i][i>>6] |= 1ull << ( i & 63 ) ;
}
while ( ! q.empty () ) q.pop () ;
rep ( i , 0 , tot - 1 ) if ( ! ind[i] ) q.push ( i ) ;
while ( ! q.empty () ) {
int j = q.front () ; q.pop () ;
for (int i = head2[j] ; i ; i = G[i].next) {
int k = G[i].to ;
-- ind[k] ; update ( j , k ) ;
if ( ! ind[k] ) q.push ( k ) ;
}
}
while ( qs -- ) {
int u , v ; io >> u >> v ; u = idx[u] ; v = idx[v] ;
if ( ( bit[u][v>>6] >> ( v & 63 ) ) & 1 ) puts ("Yes") ;
else puts ("No") ;
}
system ("pause") ; return 0 ;
}