• [CF GYM101741E] Code-Cola Plants


    前言

    新性质get!

    题目

    CF

    多组数据,给一张 (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;
    }
    
  • 相关阅读:
    Move Zeroes
    Intersection of Two Arrays II
    Intersection of Two Arrays
    Create Maximum Number
    Leetcode version 2
    JDBC学习笔记
    九章算法基础班
    九章算法强化班
    LeetCode
    Amazon High Frequent 9 problems
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15478001.html
Copyright © 2020-2023  润新知