题解 CF555E 【Case of Computer Network】

思路:

(quad)先缩点(找边双),注意一条边只能走一次,缩完点后图就变成了一棵树(或森林,可能有不连通的情况,注意要额外记录是否在一个连通块中)。

(quad)对于每一个询问 (x) -> (y) ,若在一个强连通分量中(边双)不考虑,直接跳过,若不在一个连通块(树)中,直接输出 ("No") ,否则对 (x)(y) 的路径修改,从x向上跳是修改 (up) 数组,从 (y) 向上跳时修改 (down) 数组,在同一条链上时判断深度,注意方向是从 (x)(y) 的方向,修改的是边权最后一次跳要 (+1) ,最后判断每一条边是否只有向上跳或向下跳(边的方向),若两个都有就输出 ("No") ,有矛盾,最后输出 ("Yes") 即可。

前置知识:(Tarjan) 缩点 (+) 树链剖分 (+) 线段树

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define re register int
#define int long long
#define LL long long
#define il inline
#define next nee
#define inf 1e18
il int read()
{
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
  return x*f;
}
il void print(int x)
{
  if(x<0)putchar('-'),x=-x;
  if(x/10)print(x/10);
  putchar(x%10+'0');
}
const int N=2e5+5;
int n,m,q1,next[N<<1],go[N<<1],u[N],v[N],head[N],tot,dfn[N],top[N];
int size[N],cnt,c[N],dep[N],low[N],d[N],father[N],son[N],seg[N];
bool b[N<<1],flag,up[N<<2],down[N<<2];
stack<int>q;
il void Add(int x,int y)
{
  next[++tot]=head[x];
  head[x]=tot;
  go[tot]=y;
}
il void Tarjan(int x,int fa)//缩点
{
  dfn[x]=low[x]=++cnt;d[x]=fa;
  q.push(x);
  for(re i=head[x];i;i=next[i])
    {
      if(b[i]||b[i^1])continue;b[i]=1;
      int y=go[i];
      if(!dfn[y])Tarjan(y,fa),low[x]=min(low[x],low[y]);
      else if(!c[y])low[x]=min(low[x],dfn[y]);
    }
  if(dfn[x]==low[x])
    {
      c[x]=++c[0];
      while(q.top()!=x)
	{
	  c[q.top()]=c[0];
	  q.pop();
	}
      q.pop();
    }
}
il void dfs1(int x,int fa)
{
  dep[x]=dep[fa]+1;father[x]=fa;size[x]=1;
  for(re i=head[x];i;i=next[i])
    {
      int y=go[i];
      if(y==fa)continue;
      dfs1(y,x);
      size[x]+=size[y];
      if(size[y]>size[son[x]])son[x]=y;
    }
}
il void dfs2(int x,int topf)
{
  top[x]=topf;seg[x]=++seg[0];
  if(!son[x])return;
  dfs2(son[x],topf);
  for(re i=head[x];i;i=next[i])
    {
      int y=go[i];
      if(top[y])continue;
      dfs2(y,y);
    }
}
il void pushdown(int k,int l,int r,int mid)
{
  if(down[k])down[k<<1]=down[k<<1|1]=1;
  if(up[k])up[k<<1]=up[k<<1|1]=1;
}
il void change1(int k,int l,int r,int x,int y,int z)
{
  if(x<=l&&y>=r){if(z==1)down[k]=1;else up[k]=1;return;}
  int mid=l+r>>1;
  if(down[k]||up[k])pushdown(k,l,r,mid);
  if(x<=mid)change1(k<<1,l,mid,x,y,z);
  if(y>mid)change1(k<<1|1,mid+1,r,x,y,z);
}
il void change(int x,int y)
{
  int fx=top[x],fy=top[y];
  while(fx!=fy)
    {
      if(dep[fx]>dep[fy]){
	change1(1,1,c[0],seg[fx],seg[x],2);
	x=father[fx];fx=top[x];
      }
      else {
	change1(1,1,c[0],seg[fy],seg[y],1);
	y=father[fy];fy=top[y];
      }
    }
  if(seg[y]+1<=seg[x])change1(1,1,c[0],seg[y]+1,seg[x],2);
  else if(seg[x]+1<=seg[y])change1(1,1,c[0],seg[x]+1,seg[y],1);
}
il bool check(int k,int l,int r)//最后检查每一条边
{
  if(l==r){if(down[k]&&up[k])return 0;else return 1;}
  int mid=l+r>>1;
  if(down[k]||up[k])pushdown(k,l,r,mid);
  return min(check(k<<1,l,mid),check(k<<1|1,mid+1,r));
}
signed main()
{
  n=read();m=read();q1=read();tot=1;
  for(re i=1;i<=m;i++)
    {
      re x=read(),y=read();
      u[i]=x;v[i]=y;
      Add(x,y);Add(y,x);
    }
  for(re i=1;i<=n;i++)
  if(!dfn[i])Tarjan(i,i);//缩点
  memset(next,0,sizeof(next));
  memset(head,0,sizeof(head));
  memset(go,0,sizeof(go));
  tot=0;
  for(re i=1;i<=m;i++)
    {
      int x=c[u[i]],y=c[v[i]];
      if(x!=y)Add(x,y),Add(y,x);
    }
  for(re i=1;i<=n;i++)//树剖预处理
    if(!dep[i])dfs1(i,0),dfs2(i,i);//可能有多棵树
  for(re i=1;i<=q1;i++)
    {
      int u1=read(),v1=read();
      if(d[u1]!=d[v1]){puts("No");return 0;}//是否在一个连通块中
      if(c[u1]!=c[v1])change(c[u1],c[v1]);//若不在一个强连通分量,就修改
      }
  if(check(1,1,c[0]))puts("Yes");
  else puts("No");
  return 0;
}