前言
新性质get!
题目
多组数据,给一张 (n) 个点,(m) 条有向边的 DAG,两个点 (a,b)。试寻找两组路径:
- 两组路径都含有且仅含有 (n-1) 条不同的边。
- 从节点 (a) 经第一组路径可以到达所有点。
- 从所有点经第二组路径可以到达节点 (b) 。
(2le sum nle 5 imes 10^5;1le sum mle 10^6;1le a,ble n.)
讲解
首先我们转换题意,第一组路径是使得除节点 (a) 外的入度为 (1) 的 (n-1) 条边,第二组路径是使得除节点 (b) 外的出度为 (1) 的 (n-1) 条边。
由于不能共用边,自然可以想到二分图匹配。左边有 (2n-2) 个点,表示每个点的入度和出度,右边有 (m) 个点,表示这 (m) 条边,显然每条边匹配一个出度点和一个入度点。
我们要做的就是二分图匹配,但是无论是匈牙利还是网络流,在如此大的数据范围下都不太能跑,于是考虑挖掘性质。
发现右边其实就只有两条边,考虑直接把右边的点删掉,左边直接连边,由于右边的点度数至多为2,所以最后连出来是基环树森林。是基环树这事我想了好久qwq
然后直接在基环树上DP就好了,或者说贪心?
时间复杂度 (O(n+m))。
代码
注意初始化!!!
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 1000005;
int n,m,A,B;
LL Read()
{
LL x = 0,f = 1; char c = getchar();
while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int head[MAXN],tot;
struct edge
{
int v,nxt;
}e[MAXN<<1];
void Add_Edge(int x,int y)
{
e[++tot] = edge{y,head[x]};
head[x] = tot;
}
void Add_Double_Edge(int x,int y)
{
Add_Edge(x,y);
Add_Edge(y,x);
}
int ans[MAXN];
bool vis[MAXN],un[MAXN];
void dfs(int x,int ifa)
{
vis[x] = 1;
for(int i = head[x],v; i ;i = e[i].nxt)
{
v = e[i].v;
if((i^1) == ifa) continue;
if(!vis[v]) dfs(v,i);
if(un[x]) continue;
if((i>>1)^ans[v]) ans[x] = i>>1;
}
if(!ans[x] && !un[x] && ifa) ans[x] = ifa>>1;//have no alternative
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
while(~scanf("%d",&n))
{
m = Read(); A = Read(); B = Read(); tot = 1;
for(int i = (n<<1);i >= 1;-- i) ans[i] = head[i] = 0,vis[i] = un[i] = 0;
for(int i = 1,u,v;i <= m;++ i)
{
u = Read(),v = Read();
Add_Double_Edge(u,v+n);
}
un[A+n] = un[B] = 1;//A:no in,B: no out
for(int i = 1;i <= (n<<1);++ i)
if(!vis[i]) dfs(i,0);
bool no = 0;
for(int i = 1;i <= (n<<1);++ i)
if(!un[i] && !ans[i])
{
no = 1;
break;
}
if(no) printf("NO
");
else
{
printf("YES
");
for(int i = n+1;i <= (n<<1);++ i) if(!un[i]) Put(ans[i],' '); putchar('
');
for(int i = 1;i <= n;++ i) if(!un[i]) Put(ans[i],' '); putchar('
');
}
}
return 0;
}