zoukankan      html  css  js  c++  java
  • Codeforces 1364D Ehab's Last Corollary

    Description

    给出一张 $n$ 个点的无向连通图和一个常数 $k$。

    你需要解决以下两个问题的任何一个:

    1. 找出一个大小为 $lceilfrac{k}{2} ceil$的独立集。
    2. 找出一个大小不超过 $k$ 的环。

    独立集是一个点的集合,满足其中任意两点之间在原图上没有边直接相连。

    $3 le k le n le 10^5$,$n - 1le m le 2 imes 10^5$

    Solution

    想一想,「简单环」和「独立集」有什么关系?

    如果一个大小为 $s$ 的环是不可分割的,那么,把环上的元素隔一位取一个,就可以分为两个大小为 $lfloorfrac{s}{2} floor$ 的独立集。

    下图展示的是 $s = 5$ 的一种分法,分割出了 ${1, 3}$ 和 ${ 2, 4}$ 两个独立集:

    关键在于找出一个「不可分割的环」,比方说它的大小为 $a$,要是 $a le k$,那么我们就完成任务 2 了,直接输出它就好,否则($a > k$),必然有 $lfloorfrac{a}{2} floor ge lceilfrac{k}{2} ceil$,这意味着,在这个分割出来的独立集中再分割一个大小为 $lceilfrac{k}{2} ceil$ 的独立集就可以完成任务 1 了。

    所以,如果求出一个不可分割的环呢?

    我们可以先在这个图中随便找出一个环,把点集按照顺序存在一个  deque (双端队列)里面。

    然后,遍历每一条边 $left<u, v ight>$,如果 $left<u, v ight>$ 切割了现有的环,那么,就再割出来的两个小环中任选一个保留,$m$ 条边都割完为止。

    具体的实现方法是,如果 $u, v$ 都在当前环中,且 $left<u, v ight>$ 不是一条环上的边(可以比较 $u$ 和 $v$ 在环中的位置差是不是 $1$ 来确定),则 $left<u, v ight>$ 必然切割当前环,如图:

    原来  deque 当中的元素依次是 ${1, 2, 3, 4, 5}$,现在来了条边 $left<2, 4 ight>$ 切割,那么,必然有一个新的小环端点为 $u = 2$ 和 $v = 4$,那么把两端其余的元素弹出,就可以得到新的小环 ${ 2, 3, 4 }$。

    当然,如果这张图一个环都没有,则这张图是树,树必然是二分图,直接二分图染色后在较大的颜色集中提取 $lceilfrac{k}{2} ceil$ 个来完成任务 1 就好了。

    初始 DFS 找环是 $mathcal O(n)$ 的,遍历边集是 $mathcal O(n)$ 的,环中最多有 $n$ 个元素,所以弹出次数的总和也是 $mathcal O(n)$ 的,所以总时间复杂度 $mathcal O(n)$(这里不区分 $n$ 和 $m$)。

    代码仅供参考:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 5;
    vector<int> G[N];
    deque<int> circ;
    vector<int> s, col[2];
    int n, m, k, pos[N], u[N], v[N];
    bool in_circ[N];
    void DFS(int u)
    {
    	pos[u] = s.size();
    	s.push_back(u);
    	col[pos[u] & 1].push_back(u);
    	for(int v : G[u])
    	{
    		if(pos[v] == -1) DFS(v);
    		else if(circ.empty() && pos[u] > pos[v] + 1)
    		{
    			for(int i = pos[v]; i <= pos[u]; i++)
    			{
    				circ.push_back(s[i]);
    				in_circ[s[i]] = true;
    			}
    		}
    	}
    	s.pop_back();
    }
    int main()
    {
    	memset(pos, -1, sizeof pos);
    	cin >> n >> m >> k;
    	for(int i = 1; i <= m; i++)
    	{
    		cin >> u[i] >> v[i];
    		G[u[i]].push_back(v[i]);
    		G[v[i]].push_back(u[i]);
    	}
    	DFS(1);
    	if(circ.empty())
    	{
    		cout << 1 << endl; 
    		if(col[0].size() < col[1].size()) swap(col[0], col[1]);
    		for(int i = 0; i < (k + 1) / 2; i++) cout << col[0][i] << ' ';
    		return 0;
    	}
    	for(int i = 1; i <= m; i++)
    	{
    		if(in_circ[u[i]] && in_circ[v[i]] && abs(pos[u[i]] - pos[v[i]]) != 1)
    		{
    			while(circ.front() != u[i] && circ.front() != v[i]) 
    				in_circ[circ.front()] = false, circ.pop_front();
    			while(circ.back() != u[i] && circ.back() != v[i]) 
    				in_circ[circ.back()] = false, circ.pop_back();
    		}
    	}
    	if(circ.size() <= k)
    	{
    		cout << 2 << endl;
    		cout << circ.size() << endl;
    		for(int i = 0; i < circ.size(); i++) cout << circ[i] << ' ';
    	}
    	else
    	{
    		cout << 1 << endl;
    		for(int i = 0; i < (k + 1) / 2; i++) cout << circ[i * 2] << ' ';
    	}
    	return 0;
    }
  • 相关阅读:
    07.进程管理+作业控制+文件查找与压缩+文件压缩与打包+tar打包解包+NFS
    反转数字
    取出列表中第N大的数
    nginx负载均衡
    DNS解析
    浅谈Http长连接和Keep-Alive以及Tcp的Keepalive
    django_orm操作
    多线程下的单例模式
    装饰器实现单例模式
    Django model 中的 class Meta 详解
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1364D.html
Copyright © 2011-2022 走看看