LOJ #2540. 「PKUWC 2018」隨機算法(概率dp)


題意

LOJ #2540. 「PKUWC 2018」隨機算法

題解

朴素的就是 \(O(n3^n)\) dp 寫了一下有 \(50pts\) ... 大概就是每個點有三個狀態 , 考慮了但不在獨立集中 , 考慮了並且在獨立集中 , 還沒考慮 . 轉移就很顯然了 qwq

然后要優化嘛 , 把其中兩個狀態合起來 , 也就是分成考慮了和沒考慮了的兩種 .

其中考慮了的那種 , 只會存在兩種狀態 , 要么是在獨立集內 , 要么就是與獨立集聯通 , 沒有考慮的 絕對不和獨立集聯通 就行了 .

然后我們枚舉一個集合 , 考慮強制把一個點選進來 . 如果要選它 , 那么它周圍的一圈都不能去選 .

為了使這個 dp 不存在后效性 , 我們不能讓之后的選的點連得點存在於獨立集中 , 我們把他周圍一圈的都放進來就行了 .

也就是說當前維護的集合 , 會被最外面沒有存在於獨立集中的一圈給包圍住 .

然后連上來的時候會有很多種排列的方式 , 直接乘上一個排列數就行了 . (相當於預留位置)

最后算答案因為是概率 , 除以 \(\frac{1}{n!}\) 就行了 .

這些我都是問 DOFY 才懂的 , 還是太菜啦 ~

具體來說 方程是這樣的 (\(\displaystyle f_{i,s}\) 當前選了 \(i\) 個點 , 考慮過的集合是 \(s\) , 與 \(k\) 相鄰的所有點(包括它自己)的集合為 \(w_k\) ):

\[[k \notin s] ~ \displaystyle f_{i,s} \times A_{n-|s|-1}^{|w_k-w_k \cap s|} \to f_{i+1,s \cup w_k} \]

時間復雜度是 \(O(n^2 2^n)\) 可以通過此題了 .

然后進行一波優化 , 對於一個確定的考慮過的集合 \(s\) 那么它選取最大獨立集 , 是選取全局的最大獨立集的必要條件 .

(這個應該顯然吧 ... 因為最外圈你不要的話 , 如果里面不是最大獨立集的話 , 外面取得最大也達不到全局最大)

那么第一位考慮選點的就可以不要了 , 時間復雜度就是 \(O(n2^n)\) .

這樣寫了一下 莫名奇妙就是 LOJ 最快的了 ?

代碼

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
	freopen ("2540.in", "r", stdin);
	freopen ("2540.out", "w", stdout);
#endif
}

const int N = 20;
int n, m, Con[N];

typedef long long ll;
const int Mod = 998244353;

ll fpm(ll x, int power) {
	ll inv = 1;
	for (; power; power >>= 1, (x *= x) %= Mod)
		if (power & 1) (inv *= x) %= Mod;
	return inv;
}

ll fac[N + 5], ifac[N + 5];
void Init(int maxn) {
	fac[0] = ifac[0] = 1;
	For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod;
	ifac[maxn] = fpm(fac[maxn], Mod - 2);
	Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
}

int dp[1 << N], bit[1 << N], MaxSize[1 << N];;

inline int A(int n, int m) { if (m > n || n < 0 || m < 0) return 0; return fac[n] * ifac[n - m] % Mod; }

int main () {
	File();

	n = read(); m = read(); Init(n); 
	For (i, 1, m) {
		int u = read() - 1, v = read() - 1;
		Con[u] |= (1 << v);
		Con[v] |= (1 << u);
	}

	For (i, 0, n - 1) Con[i] |= (1 << i);

	dp[0] = 1;
	int maxsta = (1 << n) - 1;
	For (i, 0, maxsta) bit[i] = bit[i >> 1] + (i & 1);

	For (i, 0, maxsta) if (dp[i]) {
		For (j, 0, n - 1) if (!((1 << j) & i)) {
			register int Next = i | Con[j];
			if (chkmax(MaxSize[Next], MaxSize[i] + 1)) dp[Next] = 0;
			if (MaxSize[Next] == MaxSize[i] + 1) (dp[Next] += 1ll * dp[i] * A(n - bit[i] - 1, bit[Con[j] ^ (Con[j] & i)] - 1) % Mod) %= Mod;
		}
	}

	printf ("%lld\n", 1ll * dp[maxsta] * ifac[n] % Mod);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM