【CodeForces 1106D】优先队列 | BFS | CGWR | E

1106D. Lunar New Year and a Wander

time limit per test: 2 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output

Tags:BFS Dijkstra 堆/优先队列

题目描述

给定一个有 V V V 个点 E E E 条边的无向连通图(顶点编号从 1 1 1 V V V可有重边自环

L u n a r Lunar Lunar 这个人从顶点 1 1 1 出发,然后在图中任意游荡(只要沿着图中存在的边走就行,可以往返走),每当她第一次访问到某个点时输出此点的编号。那么当她把每个点都至少访问了一遍后就会得到一个 1 1 1 V V V 的排列。求所有可能存在的排列中字典序最小者



输入

1 1 1 组。第一行两个数 V V V E E E,代表顶点个数和边的条数

接下来 E E E 行每行 2 2 2 个数代表一条无向边连接的两个顶点

范围: 1 ≤ V ≤ 1 0 5 1\le V \le 10^5 1V105 ;   1 ≤ E ≤ 1 0 5 1\le E \le 10^5 1E105


输出

一个 1 1 1 V V V 的排列(数字之间打一个空格),应是所有走法可能中字典序最小者。


输入样例 1

5 5
1 4
3 4
5 4
3 2
1 5

输出样例 1

1 4 3 2 5



输入样例 2

10 10
1 4
6 8
2 5
3 7
9 4
5 6
3 4
8 10
8 9
1 10

输出样例 2

1 4 3 7 9 8 6 5 2 10 



分析

这道题其实挺直觉的。每次从已访问子图(已经访问过的点所构成的连通图)的邻接的一批顶点中选一个编号最小的扩展过去,按这样的顺序把每个点都访问到就得到了最小字典序的答案。是不是就是按一定顺序的 B F S BFS BFS ?(其实跟 D i j k s t r a Dijkstra Dijkstra 也有异曲同工之妙,因为二者都是每次从已访问子图的邻接点中挑一个扩展过去,只不过 D i j k s t r a Dijkstra Dijkstra 是选离已访问子图最近的点,而本题是选编号最小的点。)

又因为这里是稀疏图,所以邻接表存图就行。用最小堆维护进行带优先级的 B F S BFS BFS B F S BFS BFS 序即为答案。

时间复杂度:循环 Θ ( V ) \Theta (V) Θ(V) 次,每次循环出队是 O ( log ⁡ V ) O(\log V) O(logV) 的,入队则是所有循环均摊 O ( E log ⁡ V ) O(E \log V) O(ElogV),总的 O ( ( V + E ) log ⁡ V ) O((V+E) \log V) O((V+E)logV)



【 C G 】 【CG】 CG

这道题的易错点(针对我个人)在哪呢?主要是别偷懒 c o n s t e x p r   i n t   M V constexpr\ int\ MV constexpr int MV c o n s t e x p r   i n t   M E constexpr\ int\ ME constexpr int ME 一定都要写,不要偷懒只写一个 M V MV MV

  • 有时候点和边的范围不同
  • 其实范围不同时还会强迫我去写俩定义,但是在范围相同时,如果又来个什么双向边,或者又来个什么扩边增边改图,同时如果又用的是 链式前向星 ,那用 M V MV MV 去开 e d g e [ M V ] edge[MV] edge[MV] 就越界了。
  • 另外一个 T i p s Tips Tips:用邻接表时,开 s t d : : v e c t o r std::vector std::vector 数组时,用的确实还是 M V MV MV,而不是 M E ME ME。好像只有链式前向星涉及到 M E ME ME



AC代码

对于STL已有的数据结构呢,我一般是坚持能用STL则用STL的原则。但是对于这个优先队列,可以的话我还是习惯用自己的。
STL的优先队列速度实在不行(即便开 O 2 O_2 O2

所以不喜勿喷啦~

#include <bits/stdc++.h>

#define _F0N(i,n) for(i=0;i<n;++i)
#define _FLR(i,l,r) for(i=l;i<r;++i)
#define _gF(_1,_2,_3,_F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(i=0;i<=n;++i)
#define _FDL(i,l,r) for(i=l;i<=r;++i)
#define _gFD(_1,_2,_3,_FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define OPER1(T,x1,b1) inline bool operator<(const T&o)const{return x1 b1 o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&o)const{return x1 b1 o.x1||x1==o.x1&&x2 b2 o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&o)const{return x1 b1 o.x1||x1==o.x1&&(x2 b2 o.x2||x2==o.x2&&x3 b3 o.x3);}

#define LL long long
#define ULL unsigned long long
#define sc(x) {register char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=getchar());x*=_v;}
#define PC putchar
template<typename T>void PRT(const T a){if(a<0){PC(45),PRT(-a);return;}if(a>=10)PRT(a/10);PC(a%10+48);}
template<typename T>void UPRT(const T a){if(a>=10)UPRT(a/10);PC(a%10+48);}

#define CON constexpr
#define T_CASE int _CASE;sc(_CASE)for(int __=1;__<=_CASE;++__)
#define cincout cin.tie(0),ios::sync_with_stdio(false)
#define eps 1e-8
#define PI 3.141592653589793
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define endl '\n'
#define priority_queue priority_queue
#define PQ std::priority_queue
#define PR std::pair
#define vector vector
#define VI std::vector<int>
#define MII std::map<int,int>
#define MLI std::map<LL,int>
#define MSI std::map<std::string,int>
#define PII std::pair<int,int>
#define PLI std::pair<LL,int>
#define PSI std::pair<std::string,int>
#define MPFD(k) auto it=mp.find(k)

#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define get_max(a,l,r,_max) auto _max=a[l];for(int _i=l+1,_r=r;_i<_r;++_i)if(_max<a[_i])_max=a[_i]
#define get_min(a,l,r,_min) auto _min=a[l];for(int _i=l+1,_r=r;_i<_r;++_i)if(_min<a[_i])_min=a[_i]
#define ABS(a) (a>0?a:-a)
#define FABS(a) (a>0?a:-a)
#define log2n(x) (log(x)/0.69314718055995)

#define PB emplace_back
#define EB emplace_back
#define EK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define swap(a, b) do{auto _t=a; a=b; b=_t;}while(0)
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))

CON int MV(1e5 + 7);
CON int ME(2e5 + 7);	// 双向边,别忘了二倍

bool vis[MV];

template <class Type, int MN>
class KPQ
{
#define ChosenType Type
#define CMP <
#define _pd(_r) int p=_r;for(int c,E=cnt>>1;p<=E;p=c){c=p<<1;if(c!=cnt&&heap[c+1] CMP heap[c])++c;if(heap[c] CMP tp)heap[p]=heap[c];else break;}heap[p]=tp
	public:Type heap[2 * MN];int cnt;public:KPQ(const ChosenType MS):cnt(0){*heap=MS;}void push(const ChosenType data){register int i=++cnt;for(;data CMP heap[i>>1];i>>=1)heap[i]=heap[i>>1];heap[i]=data;}void pop(void){const ChosenType tp=heap[cnt--];_pd(1);}inline const ChosenType top(void){return heap[1];}
};
KPQ<int, MV> pq(-1);


struct Edge
{
	int next;
	int dest;
} edge[ME]; 
int head[ME], tot;


inline void add_edge(const int u, const int v)
{
	edge[++tot].next = head[u];
	edge[tot].dest = v;
	head[u] = tot;
}

inline void visit(const int index)
{
	vis[index] = true;
	pq.push(index);
}

int main()
{
	get(V, E)
	for (int i=0; i<E; ++i)
	{
		get(u, v)
		add_edge(u, v);
		add_edge(v, u);
	}

	visit(1);
	while (pq.cnt)
	{
		int u = pq.top();
		pq.pop();
		PRT(u), PC(32);

		for (int i=head[u]; i; i=edge[i].next)
		{
			const int v = edge[i].dest;
			if (not vis[v])
			{
				visit(v);
			}
		}
	}

	return 0;
}


(什么?过个年 CF 就炸了?)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值