【YBT2023寒假Day8 C】图论题(图论)(并查集)(线段树合并)

图论题

题目链接:YBT2023寒假Day8 C

题目大意

给你一个无向图,然后你会一直操作直到无法操作,每次找出一个满足条件的三元组 (a,b,c),满足 a<b<c,a,b 与 a,c 之间有边,b,c 之间没有边,然后进行操作把 b,c 连边。
然后问你最后的图有多少种用 n 种颜色染色的方案使得每条边连着的两个点颜色都不同。

思路

首先考虑如果最后的图你知道了要怎么染色。
发现按编号从小到大加入似乎没有头猪,考虑反着过来。

发现反着有一个什么优点呢,就是它可以很好的利用上性质。
那你对于一个点 \(x\),它如果跟后面的边连了 \(y_1,y_2,...,y_m\),那根据我们操作的方法,我们会知道 \(y_1,y_2,...,y_m\) 之间是两两连边的。
那它们的染色一定就是两两不同的,那你 \(x\) 这个点只要跟它们都不同就行。
那我们设 \(d_i\)\(i\) 点有多少条连向编号比 \(i\) 大的点,染色方案就是:\(\prod\limits_{i=1}^n(n-d_i)\)

那我们看怎么求 \(d_i\) 即可。


考虑我们直接看是否能找到一个 \((x,y)(x<y)\) 有边的充要条件。
你看到中间的媒介点有 \(<x,<y\) 的条件,那你考虑猜测是不是要存在一条 \(x\)\(y\) 的路径使得路径上的点除了 \(x,y\) 别的编号都 \(<x\)
考虑证明:

充分性:如果存在这么一条路径 \(x,p_1,p_2,...,p_m,y\),找到这些点中编号最小的点 \(p_k\)
那对于 \(p_{k-1},p_{k+1}\) 编号都大于它而且跟它连边。
那他两就可以连边,\(p_k\) 就可以不要了。
那最后就只会剩下 \(x,y\),也就是说明有边了。

必要性:\((x,y)\) 要么直接有边,要么就是由 \((k,x),(k,y)(k<x<y)\) 生成。
那归纳一下,\((k,x)\) 的有边要么直接有,要么再由一个编号 \(<k\) 的生成,\((k,y)\) 同理。
假设由生成的全部递归下去,最后会拆成若干条边(组成路径),而这些边它的编号限制越来越小,也就肯定小于一开始的 \(x\)


于是你设 \(A_x\) 集合是原图中所有跟 \(x\) 连边且编号比 \(x\) 大的。
\(S_i\) 则是加边之后的图的。
然后 \(S_i\) 通过上面的结论,就是 \(x\) 只走编号比 \(x\) 小的点能到达的点的 \(A_x\) 的并集中 \(>x\) 的部分。
那找 \(>x\) 的部分可以用线段树维护每个的数量。
那至于集合的并,我们可以用并查集维护边形成的连通块,然后合并的时候我们可以用线段树合并来搞。

代码

#include <bits/stdc++.h>
#define mo 998244353
#define ll long long

namespace quickio
{
	const int bufferSize = 1 << 20; 

	char br[bufferSize], *tailr = br, *headr = br; 
	inline char nextChar()
	{
		if (headr == tailr)
			tailr = (headr = br) + fread(br, 1, bufferSize, stdin); 
		return headr == tailr ? EOF : *headr++; 
	}

	char bw[bufferSize], *tailw = bw; 
	inline void printChar(const char &ch)
	{
		if (tailw == bw + bufferSize)
			fwrite(tailw = bw, 1, bufferSize, stdout); 
		*tailw++ = ch; 
	}
	inline void flush()
	{
		if (tailw != bw)
			fwrite(bw, 1, tailw - bw, stdout); 
	}

	template <class T>
	inline void read(T &x)
	{
		static char ch; 
		while (!isdigit(ch = nextChar())); 
		x = ch - '0'; 
		while (isdigit(ch = nextChar()))
			x = x * 10 + ch - '0'; 
	}

	template <class T>
	inline void putint(T x)
	{
		static char buf[15], *tail = buf; 
		if (!x)
			printChar('0'); 
		else
		{
			for (; x; x /= 10) *++tail = x % 10 + '0'; 
			for (; tail != buf; --tail) printChar(*tail); 
		}
	}
}

using quickio::read; 
using quickio::putint; 
using quickio::printChar;

using namespace std;

const int N = 1e6 + 100;
int n, m, rt[N], fa[N], d[N];
vector <int> G[N];

struct XD_tree {
	int ls[N << 6], rs[N << 6], f[N << 6], tot;
	
	void up(int now) {
		f[now] = f[ls[now]] + f[rs[now]];
	}
	
	void insert(int &now, int l, int r, int pl) {
		if (!now) now = ++tot;
		if (l == r) {
			f[now] = 1; return ;
		}
		int mid = (l + r) >> 1;
		if (pl <= mid) insert(ls[now], l, mid, pl);
			else insert(rs[now], mid + 1, r, pl);
		up(now);
	}
	
	int query(int now, int l, int r, int L, int R) {
		if (!now) return 0;
		if (L <= l && r <= R) return f[now];
		int mid = (l + r) >> 1, re = 0;
		if (L <= mid) re += query(ls[now], l, mid, L, R);
		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
		return re;
	}
	
	int merge(int x1, int x2, int l, int r) {
		if (!x1 || !x2) return x1 + x2;
		if (l == r) {
			f[x1] |= f[x2]; return x1;
		}
		int mid = (l + r) >> 1;
		ls[x1] = merge(ls[x1], ls[x2], l, mid);
		rs[x1] = merge(rs[x1], rs[x2], mid + 1, r);
		up(x1); return x1;
	}
}T;

int find(int now) {
	if (fa[now] == now) return now;
	return fa[now] = find(fa[now]);
}

int main() {
	freopen("graph.in", "r", stdin);
	freopen("graph.out", "w", stdout);
	
//	scanf("%d %d", &n, &m);
	read(n); read(m);
	for (int i = 1; i <= m; i++) {
//		int x, y; scanf("%d %d", &x, &y);
		int x, y; read(x); read(y);
		if (x < y) swap(x, y); G[x].push_back(y);
		T.insert(rt[y], 1, n, x);
	}
	
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < G[i].size(); j++) {
			int x = G[i][j];
			int X = find(i), Y = find(x);
			if (X == Y) continue;
			rt[Y] = T.merge(rt[Y], rt[X], 1, n); fa[X] = Y;
		}
		d[i] = T.query(rt[find(i)], 1, n, i + 1, n);
	}
	
	ll ans = 1;
	for (int i = 1; i <= n; i++)
		ans = ans * (n - d[i]) % mo;
	putint((int)ans);
//	printf("%lld", ans);
	
	quickio::flush();
	return 0;
}
posted @ 2023-02-08 16:01  あおいSakura  阅读(3)  评论(0编辑  收藏  举报