集合劃分 題解

2022-06-19 11:21:11 字數 2690 閱讀 8996

這道題思路挺清奇的。屬於那種既考演算法模板又考思維的一類題。

看到乙個點,可以放在\(a\)或\(b\)兩個集合,就要聯想到2-sat問題。

這道題如果只讓判斷是否存在合法方案,並輸出任意一種方案,那這題就屬於2-sat問題的模板。

但是這題比較難的一點就是求方案數。

一般的2-sat問題能不能求方案數呢?我在網上搜了一下,沒有找到滿意的答案。

那麼這道題肯定就是要挖掘題目的性質了。

考慮現在已經用普通的tarjan演算法求出來一種合法方案了,用這一種合法方案去求出其他的合法方案。那麼無非就是試圖將a集合中的元素放到b集合中,或者將b集合中的元素放到a集合中,或者交換一下a集合、b集合中的元素唄。

乙個很重要的性質:我們發現每次最多只能將a集合中的乙個元素放到b集合中。形式化一點,假設我們已經求出來一種合法方案\(s\),那麼一定不會存在這樣的合法方案\(t\),使得存在兩個元素\(x, y\), 在\(t\)這種方案下\(x, y\)在\(b\)集合中,但在\(s\)這種方案下\(x,y\)卻處於\(a\)集合中。

原因是因為如果\(x,y\)都在\(a\)集合中,那麼說明\(x,y\)之間一定有邊,那麼\(x,y\)就不能放在\(b\)集合中了。

反之,也一定不會存在這樣的合法方案\(t'\),使得存在兩個元素\(x,y\),在\(t'\)這種方案下\(x,y\)在\(a\)集合中,但在\(s\)這種方案下\(x,y\)卻處於\(b\)集合中。原因是類似的。

所以,要想從已知的合法方案\(s\),構造出其他合法方案\(t\),只有三種情況。

從\(a\)中取出乙個元素\(x\),丟到\(b\)中去。需滿足的條件:所有與\(x\)相連的點\(y\),都必須在\(a\)中。

從\(b\)中取出乙個元素\(y\),丟到\(a\)中去。需滿足的條件:所有與\(x\)不相連的點\(y\),都必須在\(b\)中。

從\(a\)中取出乙個元素\(x\),從\(b\)中取出乙個元素\(y\),交換\(x,y\)。需滿足的條件:\(\forall (x,u)\in e\),且\(u\neq y\),都有\(u\in a\);\(\forall (y, v)\notin e\),且\(v\neq x\),都有\(v\in b\)。

時間複雜度:\(o(n^2)\)。

然後呢?然後就做完了。

#include #include #include #include #include #include using namespace std;

#define filein(s) freopen(s".in", "r", stdin)

#define fileout(s) freopen(s".out", "w", stdout)

inline int read(void)

while (ch >= '0' && ch <= '9')

return f * x;

}const int maxn = 5005, maxm = maxn * maxn * 2;

int n, head[maxn << 1], tot;

bool g[maxn][maxn], ok = true;

bool ins[maxn << 1];

int dfn[maxn << 1], low[maxn << 1], idx, num, cnt, stk[maxn << 1], top;

int ans[maxn << 1];

int tmp[maxn];

int p[maxn];

struct edge

edge(int _y, int _next) : y(_y), next(_next) {}

} e[maxm];

sets;

inline void connect(int x, int y)

void tarjan(int x) else if (ins[y]) low[x] = min(low[x], dfn[y]);

}if (low[x] == dfn[x]) while (y != x);

}}inline void solve(void)

if (flag)

}else

if (flag) }}

long long sum = 0;

long long res3 = 1ll * res1 * res2;

for (int i = 1; i <= n; ++ i)

if (siz1 != 1) sum += res1;

if (siz2 != 1) sum += res2;

if (siz1 != 0 && siz2 != 0) sum += res3 + 1;

printf("%lld\n", sum);

}int main()

}for (int i = 1; i <= n; ++ i) else }}

for (int i = 1; i <= 2 * n; ++ i) if (!dfn[i]) tarjan(i);

if (!ok)

solve();

return 0;

}

集合劃分問題

集合劃分問題 問題描述 n 個元素的集合可以劃分為若干個非空子集。例如,當n 4 時,集合可以劃分為15 不同的非空子集如下 其中,集合 由 1 個子集組成 集合 由2 子集組成 集合,由3子集組 成 集合,由4 子集組成。程式設計任務 給定正整數n 和m,計算出n元素的集合可以劃分為多少 不同的由...

集合的劃分

問題描述 設s是乙個具有n個元素的集合,s a1,a2,an 現將s劃分成k個滿足下列條件的子集合s1,s2,sk 且滿足 輸入樣例 setsub.in 23 7 輸出樣例 setsub.out 4382641999117305 演算法分析 先舉個例子,設s 1,2,3,4 k 3,不難得出s有6種...

集合劃分問題

問題描述 n個元素的集合可以劃分為若干個非空子集。例如,當n 4 時,集合可以劃分為15 個不同的非空子集如下 給定正整數n,計算出n個元素的集合可以劃分為多少個不同的非空子集。所求的是bell 數 滿足遞推公式 b n 所以這道題實際求第二類stirling數 s n,m 解決思想 1.若 m 1...