首页 > 其他分享 >洛谷 P6662 [POI 2019] Przedszkole

洛谷 P6662 [POI 2019] Przedszkole

时间:2023-11-14 15:36:18浏览次数:32  
标签:typedef 洛谷 int ll fa 2019 P6662 find mod

洛谷传送门

\(k\) 染色问题。给定 \(n\) 个点 \(m\) 条边无向图,求有多少种给每个点赋点权 \(a_u \in [1, k]\) 的方案,使得 \(\forall (u, v) \in E, a_u \ne a_v\)。

Subtask \(1\):\(n \le 15\)。

考虑因为最终只会用到最多 \(n\) 种颜色,所以设恰好用了 \(t\) 种颜色,把 \(k\) 种颜色映射到 \([1, t]\) 中,方案数为 \(\binom{k}{t}\);然后再状压 dp,设 \(f_{i, S}\) 表示已经用了前 \(i\) 种颜色,当前 \(S\) 集合中的点已经染色。转移枚举 \(S\) 的严格超集即可。时间复杂度 \(O(n3^n + qn)\)。

Subtask \(2\):\(m \le 24\)。

实际上这是 ABC294Ex K-Coloring。 考虑一些与边数有关的指数级算法。考虑容斥,钦定 \(S \subseteq E\) 中的 \((u, v)\) 必须满足 \(a_u = a_v\)。设最后连完 \(S\) 中的边后连通块数为 \(c\),答案即 \(\sum\limits_{S \subseteq E} (-1)^{|S|} k^c\)。对于每个 \(c\) 预处理有多少个边集最后会形成 \(c\) 个连通块,即可 \(O(m)\) 回答单次询问。同时因为 \(m\) 很大要注意实现精细。可以 dfs 枚举每条边是否进入 \(S\),一个简单的剪枝是递归到一条边时 \(u, v\) 在同一个连通块就立刻退出,因为选和不选的贡献抵消。时间复杂度 \(O(n2^m + qm \log n)\)。

Subtask \(3\):每个连通块是一个环。

这是 ABC307E Distinct Adjacent。每个环相互独立,所以只用考虑一个环,最后方案数乘起来。设 \(f_n\) 为 \(n\) 个点的环的方案数,转移考虑删掉点 \(n\),如果点 \(1\) 和点 \(n - 1\) 颜色相同那么把它们缩起来,同时点 \(n\) 有 \(n - 1\) 种选颜色的方案;否则点 \(n\) 有 \(k - 2\) 种选颜色的方案,因为点 \(1\) 和点 \(k - 1\) 颜色不同所以可以直接删掉点 \(n\)。因此有 \(f_n = (k - 1) f_{n - 2} + (k - 2) f_{n - 1}\)。这个东西还有通向公式 \(f_n = (k - 1)^n + (k - 1) (-1)^n\)。时间复杂度 \(O(nq + m)\)。

code
// Problem: P6662 [POI 2019] Przedszkole
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6662
// Memory Limit: 128 MB
// Time Limit: 1500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 100100;
const ll mod = 1000000007;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, m, q;
pii E[maxn];
vector<int> G[maxn];

inline void upd(ll &x, ll y) {
	((x += y) >= mod ? x -= mod : 0);
}

namespace Sub1 {
	const int maxm = (1 << 15) + 50;
	
	ll f[19][maxm], fac[maxn], ifac[maxn];
	bool g[maxm];
	
	inline ll C(ll n, ll m) {
		if (n < m || n < 0 || m < 0) {
			return 0;
		} else {
			ll res = ifac[m];
			for (ll i = n - m + 1; i <= n; ++i) {
				res = res * i % mod;
			}
			return res;
		}
	}
	
	void solve() {
		for (int S = 1; S < (1 << n); ++S) {
			g[S] = 1;
			for (int i = 1; i <= n; ++i) {
				if (S & (1 << (i - 1))) {
					for (int j : G[i]) {
						if (S & (1 << (j - 1))) {
							g[S] = 0;
							break;
						}
					}
				}
			}
		}
		f[0][0] = 1;
		for (int i = 0; i < n; ++i) {
			for (int S = 0; S < (1 << n); ++S) {
				if (!f[i][S]) {
					continue;
				}
				for (int T = (S + 1) | S; T < (1 << n); T = (T + 1) | S) {
					if (g[S ^ T]) {
						upd(f[i + 1][T], f[i][S]);
					}
				}
			}
		}
		fac[0] = 1;
		for (int i = 1; i <= n; ++i) {
			fac[i] = fac[i - 1] * i % mod;
		}
		ifac[n] = qpow(fac[n], mod - 2);
		for (int i = n - 1; ~i; --i) {
			ifac[i] = ifac[i + 1] * (i + 1) % mod;
		}
		while (q--) {
			ll x, ans = 0;
			scanf("%lld", &x);
			for (int i = 1; i <= min(n, x); ++i) {
				ans = (ans + C(x, i) * f[i][(1 << n) - 1]) % mod;
			}
			printf("%lld\n", ans);
		}
	}
}

namespace Sub2 {
	ll f[maxn];
	int fa[maxn];
	
	int find(int x) {
		return fa[x] == x ? x : find(fa[x]);
	}
	
	void dfs(int d, int cnt, int op) {
		if (d > m) {
			f[cnt] += op;
			return;
		}
		int x = find(E[d].fst), y = find(E[d].scd);
		if (x == y) {
			return;
		}
		fa[x] = y;
		dfs(d + 1, cnt - 1, -op);
		fa[x] = x;
		dfs(d + 1, cnt, op);
	}
	
	void solve() {
		for (int i = 1; i <= n; ++i) {
			fa[i] = i;
		}
		mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
		for (int i = 1; i <= m; ++i) {
			if (rnd() & 1) {
				swap(E[i].fst, E[i].scd);
			}
		}
		dfs(1, n, 1);
		while (q--) {
			ll x, ans = 0;
			scanf("%lld", &x);
			for (int i = n; i >= max(1LL, n - 60); --i) {
				ans = (ans + (f[i] + mod) * qpow(x, i)) % mod;
			}
			printf("%lld\n", ans);
		}
	}
}

namespace Sub3 {
	int fa[maxn], sz[maxn];
	
	int find(int x) {
		return fa[x] == x ? x : fa[x] = find(fa[x]);
	}
	
	inline void merge(int x, int y) {
		x = find(x);
		y = find(y);
		if (x != y) {
			fa[x] = y;
			sz[y] += sz[x];
		}
	}
	
	inline ll calc(ll n, ll m) {
		return (qpow(m - 1, n) + ((n & 1) ? mod - (m - 1) : m - 1)) % mod;
	}
	
	void solve() {
		for (int i = 1; i <= n; ++i) {
			fa[i] = i;
			sz[i] = 1;
		}
		for (int i = 1; i <= m; ++i) {
			merge(E[i].fst, E[i].scd);
		}
		vector<ll> vc;
		for (int i = 1; i <= n; ++i) {
			if (fa[i] == i) {
				vc.pb(sz[i]);
			}
		}
		while (q--) {
			ll x;
			scanf("%lld", &x);
			ll ans = 1;
			for (ll y : vc) {
				ans = ans * calc(y, x) % mod;
			}
			printf("%lld\n", ans);
		}
	}
}

void solve() {
	scanf("%lld%lld%lld", &n, &m, &q);
	for (int i = 1, u, v; i <= m; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
		E[i] = mkp(u, v);
	}
	if (n <= 15) {
		Sub1::solve();
		return;
	}
	if (m <= 24) {
		Sub2::solve();
		return;
	}
	Sub3::solve();
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:typedef,洛谷,int,ll,fa,2019,P6662,find,mod
From: https://www.cnblogs.com/zltzlt-blog/p/17831725.html

相关文章

  • GEE数据集——2019、2020、2021、2022和2023年全球固定宽带和移动(蜂窝)网络性能Shapefi
    全球固定宽带和移动(蜂窝)网络性能¶全球固定宽带和移动(蜂窝)网络性能,分配给缩放级别16网络墨卡托图块(赤道处约610.8米x610.8米)。数据以Shapefile格式和ApacheParquet格式提供,其几何形状以众所周知的文本(WKT)表示,投影在EPSG:4326中。下载速度、上传速度和延迟是通过......
  • 同一用户名,远程连接Windows Server 2019 时,如何禁止打开新窗口
    同一用户名,远程连接WindowsServer2019时,如何禁止打开新窗口答:您好!如果您想在远程连接WindowsServer2019时禁止打开新窗口,您可以尝试以下方法:使用组策略编辑器:打开组策略编辑器,可以通过运行"gpedit.msc"命令来打开。导航到"计算机配置">"管理模板">"Windows组件">"远......
  • 【洛谷 P1307】[NOIP2011 普及组] 数字反转 题解(字符串)
    [NOIP2011普及组]数字反转题目描述给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例2)。输入格式一个整数。输出格式一个整数,表示反转后的新数。样例#1样例输入#1123样......
  • 洛谷 P9129 [USACO23FEB] Piling Papers G
    第一问是简单的,\(2(n-1)-[T=1]\cdot\max\limits_{i=1}^{n}\{dep_i\}\)。对于第二问:设\(f(u)\)表示要求起点和终点均为\(u\)的情况下从\(1\)时刻开始遍历完以\(u\)为根的子树的最小花费,\(g(u)\)表示要求起点为\(u\),重点深度最大的情况下从\(1\)时刻开始遍......
  • 洛谷P8599.带分数
    这道题可以应用数位dp的思想:既然根据限制条件算出符合条件的数很难,如同大海捞针,那我就直接拿可能用到的数字,按数位把它拼出来,反而还更快。对于这道题,三个数字就是1到9全排列的三段,我们只要对每个排列,枚举分段方式即可。#include<stdio.h>#include<algorithm>#include<ios......
  • 洛谷P8598.票据
    简单题,输入有技巧注意要在正式读入之前先用一个getline把那个回车读入了,因为cin不会处理回车。#include<iostream>#include<algorithm>#include<stdlib.h>#include<cstring>#include<sstream>usingnamespacestd;constintN=1e5+5;intt,n,a[N];strings......
  • 洛谷P8597.翻硬币
    用一个队列模拟,每次都只处理队头位置的差异#include<iostream>#include<algorithm>#include<stdlib.h>#include<cstring>usingnamespacestd;constintN=1005;chars[N],t[N];intpos[N],hh,cnt,tt;intmain(){scanf("%s%s",s......
  • 2019 CSP-J
    P5661[CSP-J2019]公交换乘 就是模拟,注意车票还有使用时间限制,所以在记录坐地铁的时候就要设置时限,如果坐公交车的时间过了所有优惠票那就不能坐,而且也要记录最左边可以用的车票位置#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<vecto......
  • 洛谷 NOIP 2023 模拟赛 T2 汪了个汪
    洛谷NOIP2023模拟赛T2汪了个汪考试建出正解图不知道怎么处理,题解区樱雪喵博客薄纱。樱雪喵题解链接Ps:笔者语文爆炸,不建议阅读本文思路首先你会发现,一共有\(\frac{n(n-1)}{2}\)个二元组,有\(\frac{n(n-1)}{2}\)个横向相邻数对。按照题目要求,一个横向数对对应一个二......
  • 231112洛谷模拟赛
    T1种树P9836种树只需要运用一些小学奥数即可解决首先需要知道的一点是将\(p_i\)质因数分解后得到\(p_i=\prod\limits_{j=1}^ma_j^{k_j}\),则有\(\prod\limits_{i=1}^{m}(k_i+1)\)个因数则最终就是把所有的都再乘起来考虑\(w\)分解后能造成什么影响,依据乘法......