[CF GYM101741E] Code-Cola Plants

前言

新性质get!

题目

CF

多组数据,给一张 \(n\) 个点,\(m\) 条有向边的 DAG,两个点 \(a,b\)。试寻找两组路径:

  • 两组路径都含有且仅含有 \(n-1\) 条不同的边。
  • 从节点 \(a\) 经第一组路径可以到达所有点。
  • 从所有点经第二组路径可以到达节点 \(b\)

\(2\le \sum n\le 5\times 10^5;1\le \sum m\le 10^6;1\le a,b\le 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\n");
		else 
		{
			printf("YES\n");
			for(int i = n+1;i <= (n<<1);++ i) if(!un[i]) Put(ans[i],' '); putchar('\n');
			for(int i = 1;i <= n;++ i) if(!un[i]) Put(ans[i],' '); putchar('\n');
		}
	}
	return 0;
}
posted @ 2021-10-28 22:12  皮皮刘  阅读(52)  评论(0编辑  收藏  举报