题意:
分析:
-
前置 (Lemma) :
一个图是森林的充要条件是对于每一个子图都满足 (|E|le 2|V|-2)
-
证明:
必要性:一个图在极端情况下会分成两个树,此时任何一条边都会使一个树变成基环树
充分性:如果一个子图不满足次性质,那么该子图一定带环,不是森林
我们按照上面的定理,发现边的价值为 1,点的价值为 -2,选一条边必须选一个点,如果一个子图的权值大于 -2 那么不是森林
对于带依赖关系,选出权值和最大这种条件,我们很容易联想到最大权闭合子图
我们建出图之后发现,有的情况下选出来的权值和为 -1,但不选的最大和值可能是 0,所以我们得强制有的点必选,这样直接做的复杂度是 (O(tn^3m)) 复杂度过高,我们发现每次强制选点和之前的图相比最多改变了两条边,所以我们采取退流的做法,这样将复杂度降到了 (O(tn^2m))
代码:
#include<bits/stdc++.h>
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
inl int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 6005;
const int maxm = 5e5+5;
const int inf = 1e9+7;
int n,m,cnt=1;
int head[maxn],id[maxn],cur[maxn],dep[maxn];
queue<int> q;
struct edge
{
int to,nxt,w;
}e[maxm];
inl void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
inl void add_edge(int u,int v,int w)
{
add(u,v,w);add(v,u,0);
}
inl bool bfs(int st,int ed)
{
for(reg int i=0;i<=n+m+1;i++) dep[i]=-1;
q.push(st);dep[st]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(reg int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(e[i].w&&dep[v]==-1)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[ed]!=-1;
}
int dfs(int u,int ed,int flow)
{
if(u==ed||!flow) return flow;
int used=0,w;
for(reg int &i=cur[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1)
{
w=dfs(v,ed,min(e[i].w,flow-used));
e[i].w-=w;
e[i^1].w+=w;
used+=w;
if(used==flow) return flow;
}
}
if(!used) dep[u]=-1;
return used;
}
inl int dinic(int st,int ed,int f=inf)
{
int res=0;
while(f>res&&bfs(st,ed))
{
memcpy(cur,head,sizeof(head));
res+=dfs(st,ed,f-res);
}
return res;
}
void work()
{
cnt=1;memset(head,0,sizeof(head));
n=read();m=read();
int st=0,ed=n+m+1;
for(reg int i=1;i<=m;i++)
{
add_edge(n+i,read(),inf);
add_edge(n+i,read(),inf);
add_edge(st,n+i,1);
}
for(reg int i=1;i<=n;i++) add_edge(i,ed,2),id[i]=cnt;
int ans=dinic(st,ed);
for(reg int i=1;i<=n;i++)
{
int flow=e[id[i]].w;
e[id[i]].w=e[id[i]^1].w=0;
if(flow) ans-=dinic(i,st,flow);
if(i>1) e[id[i-1]^1].w=2;
ans+=dinic(st,ed);
if(m-ans>0)
{
puts("No");
return ;
}
}
puts("Yes");
}
}
int main()
{
int t;cin>>t;
while(t--) zzc::work();
return 0;
}