# 引言

对于acm常有一些题目让人十分棘手，并且没有专门的



### 图论

\\加入vis优化后(避免点的重复访问)运行速度实测为734ms
#include <bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define pi pair<int,int>
#define mk(a,b) make_pair(a,b)
#define mygc(c) (c)=getchar()
#define mypc(c) putchar(c)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 100005;
const int maxm = 100;
const int inf = 2147483647;
typedef long long ll;
const double eps = 1e-9;
const long long INF = 9223372036854775807ll;
ll qpow(ll a,ll b,ll c){ll ans=1;while(b){if(b&1)ans=ans*a%c;a=a*a%c;b>>=1;}return ans;}
inline void rd(int *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(ll *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(db *x){scanf("%lf",x);}
inline int rd(char c[]){int i,s=0;for(;;){mygc(i);if(i!=' '&&i!='\n'&&i!='\r'&&i!='\t'&&i!=EOF) break;}c[s++]=i;for(;;){mygc(i);if(i==' '||i=='\n'||i=='\r'||i=='\t'||i==EOF) break;c[s++]=i;}c[s]='\0';return s;}
inline void rd(int a[],int n){FOR(i,0,n)rd(&a[i]);}
inline void rd(ll a[],int n){FOR(i,0,n)rd(&a[i]);}
template <class T, class S> inline void rd(T *x, S *y){rd(x);rd(y);}
template <class T, class S, class U> inline void rd(T *x, S *y, U *z){rd(x);rd(y);rd(z);}
template <class T, class S, class U, class V> inline void rd(T *x, S *y, U *z, V *w){rd(x);rd(y);rd(z);rd(w);}
inline void wr(int x){if(x < 10) putchar('0' + x); else wr(x / 10), wr(x % 10);}
inline void wr(int x, char c){int s=0,m=0;char f[10];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(ll x, char c){int s=0,m=0;char f[20];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(db x, char c){printf("%.15f",x);mypc(c);}
inline void wr(const char c[]){int i;for(i=0;c[i]!='\0';i++)mypc(c[i]);}
inline void wr(const char x[], char c){int i;for(i=0;x[i]!='\0';i++)mypc(x[i]);mypc(c);}
template<class T> inline void wrn(T x){wr(x,'\n');}
template<class T, class S> inline void wrn(T x, S y){wr(x,' ');wr(y,'\n');}
template<class T, class S, class U> inline void wrn(T x, S y, U z){wr(x,' ');wr(y,' ');wr(z,'\n');}
template<class T> inline void wra(T x[], int n){int i;if(!n){mypc('\n');return;}FOR(i,0,n-1)wr(x[i],' ');wr(x[n-1],'\n');}
int n,m,dfn[maxn],st[maxn],top,tot,low[maxn],id[maxn],idtot,deg[maxn],inst[maxn],mark[maxn],vis[maxn];
vector<int>g[maxn],h[maxn];
set<int>s[maxn];
pi a[maxn];
void Tarjan(int u){//缩点
inst[u]=1;
low[u]=dfn[u]=++tot;
st[top++]=u;
FOR(i,0,g[u].size()){
int v=g[u][i];
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}else if(inst[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
++idtot;
int v;
do{
v=st[--top];
id[v]=idtot;
inst[v]=0;
}while(v!=u);
}
}
int tim=0;//时间戳，每次dfs使用不同的时间戳可以避免对vis数组清空
void dfs(int u,int ori,int tm){//对ori点进行预处理，使得s[ori]集合中存放ori能够到达的点(在缩点之后的图上跑)
s[ori].insert(u);
vis[u]=tim;
FOR(i,0,h[u].size()){
int v=h[u][i];
if(vis[v]==tm)continue;
dfs(v,ori,tm);
}
}
void pre(){//预处理的操作，选取度数前sqrt(n)大的点预处理出它们能够在图中遍历到的所有点
FOR(i,1,idtot+1)a[i]=mk(deg[i],i);
sort(a+1,a+1+idtot,greater<pi >());
int sq=sqrt(idtot);
FOR(i,1,sq+1){
mark[a[i].se]=1;
dfs(a[i].se,a[i].se,++tim);
}
}
int qry(int u,int tar,int tm){//查询操作
if(u==tar)return 1;//如果遇到了目标节点直接返回true
if(mark[u])return s[u].count(tar);//如果遇到了预处理过的节点就查询其集合中是否存在目标节点
FOR(i,0,h[u].size()){//如果两种情况都不符合就继续深搜，大约每sqrt(n)次搜索遇到一个预处理过的节点
int v=h[u][i];
if(vis[v]==tm)continue;//vis数组避免重复访问，缩点的时候回出现大量的重边
if(qry(v,tar,tm))return 1;
}
return 0;
}
int main(){
rd(&n,&m);
FOR(i,0,m){
int u,v;
rd(&u,&v);
g[u].push_back(v);
}
FOR(i,0,n)if(!dfn[i])Tarjan(i);
FOR(i,0,n){
FOR(j,0,g[i].size()){
int u=i,v=g[i][j];
if(id[u]==id[v])continue;
h[id[u]].push_back(id[v]);//建立缩点后的新图，注意到缩点的新图上会有大量重边，因此开一个vis数组来避免重复访问是非常有效的优化方式
deg[id[u]]++;
deg[id[v]]++;
}
}
pre();//对度数前sqrt(n)大的点暴力预处理
int q;
rd(&q);
while(q--){
int u,v;
rd(&u,&v);
}
}


05-20 186
06-21 2226

07-25 3389
09-02 1029
09-05 3193
08-02 2522
03-08 1588