题目链接:E. Case of Computer Network
题目大意:给定一张\(n\)个点\(m\)条边的无向图(图不一定联通),给定\(q\)组有向点对\((s_i,t_i)\),问是否有一种边的定向方式,使得\(q\)组点对都可以从\(s_i\)到达\(t_i\)。
题解:不会求边双的菜鸡自闭了。这一题,如果整个图是一个边双联通分量的话,因为每两个点之间都有至少两条路径,一定符合条件。那么很明显可以想到可以直接将原图边双缩点,最后得到的结果就是一片森林,如果有任意一组\((s_i,t_i)\)不在同一棵树内,直接输出No
即可,对于另外的限制,通过在在树上打标记来实现,具体操作就是:令\(lca_i=\text{LCA}(s_i,t_i)\),那么就是从\(s_i\)到\(lca_i\)的边全部向上,从\(lca_i\)到\(t_i\)的边全部向下,所以用两个标记\(tag_{1,u},tag_{2,u}\),一个\(u\)-->\(fa_u\)边的方向向上的点对数,另一个表示\(fa_u\)-->\(u\)边的方向向下的点对数,所以,在一个点上如果\(tag_{1,u}>0\text{且}tag_{2,u}>0\)时,很明显出现矛盾,输出No
,如果所有的点都合法,那么输出Yes
就可以了。
代码很长,但是是一个边双缩点模板+树上求LCA+树上差分,每一个部分都不难写。
接下来是代码:
#include <vector>
#include <cstdio>
using namespace std;
void read(int &a){
a=0;
char c=getchar();
while(c<'0'||c>'9'){
c=getchar();
}
while(c>='0'&&c<='9'){
a=(a<<1)+(a<<3)+(c^48);
c=getchar();
}
}
const int Maxn=200000;
const int Maxm=200000;
int n,m,q;
int col[Maxn+5],col_tot;
int head[Maxn+5],arrive[Maxm<<1|5],nxt[Maxm<<1|5],tot;
bool un_able[Maxm<<1|5];
int dfn[Maxn+5],low[Maxn+5],dfn_tot;
int bel[Maxn+5];
struct Question{
int s,t;
}qu[Maxn+5];
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
void tarjan(int u,int in_edge){
dfn[u]=low[u]=++dfn_tot;
for(int i=head[u];i;i=nxt[i]){
if(((i-1)^1)+1==in_edge){
continue;
}
int v=arrive[i];
if(!dfn[v]){
tarjan(v,i);
low[u]=min(low[u],low[v]);
}
else{
low[u]=min(low[u],dfn[v]);
}
}
}
void col_dfs(int u,int c){
col[u]=c;
for(int i=head[u];i;i=nxt[i]){
if(un_able[i]){
continue;
}
int v=arrive[i];
if(col[v]){
continue;
}
col_dfs(v,c);
}
}
vector<int> g[Maxn+5];
void bel_dfs(int u,int c){
bel[u]=c;
for(int i=0;i<(int)g[u].size();i++){
int v=g[u][i];
if(bel[v]){
continue;
}
bel_dfs(v,c);
}
}
int sum_1[Maxn+5],sum_2[Maxn+5];
int fa[18][Maxn+5],dep[Maxn+5];
void init_dfs(int u){
dep[u]=dep[fa[0][u]]+1;
for(int i=1;fa[i-1][fa[i-1][u]];i++){
fa[i][u]=fa[i-1][fa[i-1][u]];
}
for(int i=0;i<(int)g[u].size();i++){
int v=g[u][i];
if(v==fa[0][u]){
continue;
}
fa[0][v]=u;
init_dfs(v);
}
}
int find_lca(int u,int v){
if(dep[u]<dep[v]){
swap(u,v);
}
for(int i=17;i>=0;i--){
if(dep[fa[i][u]]>=dep[v]){
u=fa[i][u];
}
}
if(u==v){
return u;
}
for(int i=17;i>=0;i--){
if(fa[i][u]!=fa[i][v]){
u=fa[i][u];
v=fa[i][v];
}
}
return fa[0][u];
}
bool work_dfs(int u){
for(int i=0;i<(int)g[u].size();i++){
int v=g[u][i];
if(v==fa[0][u]){
continue;
}
if(!work_dfs(v)){
return 0;
}
sum_1[u]+=sum_1[v];
sum_2[u]+=sum_2[v];
}
return sum_1[u]==0||sum_2[u]==0;
}
int main(){
read(n),read(m),read(q);
int u,v;
for(int i=1;i<=m;i++){
read(u),read(v);
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=q;i++){
read(qu[i].s),read(qu[i].t);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i,0);
}
}
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(low[v]>dfn[u]){
un_able[i]=un_able[((i-1)^1)+1]=1;
}
}
}
for(int i=1;i<=n;i++){
if(col[i]==0){
col_dfs(i,++col_tot);
}
}
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=nxt[i]){
if(un_able[i]){
int v=arrive[i];
g[col[u]].push_back(col[v]);
}
}
}
for(int i=1;i<=col_tot;i++){
if(bel[i]==0){
bel_dfs(i,i);
}
}
for(int i=1;i<=col_tot;i++){
if(bel[i]==i){
init_dfs(i);
}
}
for(int i=1;i<=q;i++){
if(col[qu[i].s]==col[qu[i].t]){
continue;
}
qu[i].s=col[qu[i].s];
qu[i].t=col[qu[i].t];
if(bel[qu[i].s]!=bel[qu[i].t]){
puts("No");
return 0;
}
int lca=find_lca(qu[i].s,qu[i].t);
sum_1[qu[i].s]++;
sum_2[qu[i].t]++;
sum_1[lca]--;
sum_2[lca]--;
}
for(int i=1;i<=col_tot;i++){
if(bel[i]==i){
if(!work_dfs(i)){
puts("No");
return 0;
}
}
}
puts("Yes");
return 0;
}