這道題思路挺清奇的。屬於那種既考演算法模板又考思維的一類題。
看到乙個點,可以放在\(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...