洛谷 P6662 [POI 2019] Przedszkole

洛谷传送门

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

Subtask 1n15

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

Subtask 2m24

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

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

这是 ABC307E Distinct Adjacent。每个环相互独立,所以只用考虑一个环,最后方案数乘起来。设 fnn 个点的环的方案数,转移考虑删掉点 n,如果点 1 和点 n1 颜色相同那么把它们缩起来,同时点 nn1 种选颜色的方案;否则点 nk2 种选颜色的方案,因为点 1 和点 k1 颜色不同所以可以直接删掉点 n。因此有 fn=(k1)fn2+(k2)fn1。这个东西还有通向公式 fn=(k1)n+(k1)(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;
}
posted @   zltzlt  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示