NOIP2018模擬賽3:暴力(brute) 狀壓

**NOIP2018模擬賽3:暴力(brute) **

【問題描述】

某天小 D 出了一道樹上亂搞,被暴力水過了,原因是他造的數據是瞎隨的,而暴力的複雜度是 O(深度)的。小 D 是這樣構數據的:
對於 2~n 的每個點,隨機一個編號比它小的點作爲它的父親。小 D想知道在這種數據構造方法下,暴力的期望複雜度是多少。我們定義一棵樹的深度爲所有節點到根路徑上經過的點數的最大值。
n24n \le 24,答案輸出帶模和四捨五入到整數意義下的答案。

分析

這個量級的數據大概率是狀壓Dp。然而數據的狀態很難記錄。
首先不難注意到,本題深度相同的結點其實是本質相同的。而樹的深度是連續變化的。考慮記錄所有節點深度的差分數組,就可以狀壓了。
很巧妙的一個方法。差分和狀壓的確有妙不可言的緣分。

代碼

#include<bits/stdc++.h>
const int S = 16777221;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int b[30], v[30], p[30], P;
double f[2][S]; double ff[2][S];
void Clr(int c, int n) {for(int i = 0;i < n; ++i) f[c][i] = ff[c][i] = 0;}
int inv(int x) {
	int k = P - 2, r = 1;
	for(;k; k >>= 1, x = 1LL * x * x % P) if(k & 1 ) r = 1LL * r * x % P;
	return r;
}
int main() {
	freopen("brute.in","r",stdin);
	freopen("brute.out","w",stdout);
	b[0] = 1; for(int i = 1;i <= 25; ++i) b[i] = b[i - 1] << 1;
	int n = ri(); P = ri();
	f[0][1] = ff[0][1] = 1; int c = 1;
	for(int i = 1;i < n; ++i, c ^= 1) {
		Clr(c, b[i + 1]); 
		for(int s = 0, k, x;s < b[i]; ++s) if(ff[c ^ 1][s]) {
			for(k = x = 0;k < i; ++v[x], ++k) if(s & b[k]) p[++x] = k;
			for(k = 1;k < x; ++k) {
				int t = b[p[k + 1] + 1] - 1, nx = ((s ^ (s & t)) << 1) | (s & t);
				f[c][nx] = (f[c][nx] + 1LL * f[c ^ 1][s] * v[k]) % P;
				ff[c][nx] += ff[c ^ 1][s] * v[k];
			}
			f[c][s | b[i]] = (f[c][s | b[i]] + 1LL * f[c ^ 1][s] * v[x]) % P;
			ff[c][s | b[i]] += ff[c ^ 1][s] * v[x];
			for(int i = 0;i <= x; ++i) v[i] = 0;
		}
	}
	int A = 0, iv = 1; double fA = 0;
	for(int s = 1, k, x;s < b[n]; ++s) {
		for(x = k = 0; k < n; ++k) if(s & b[k]) ++x;
		A = (A + 1LL * f[c ^ 1][s] * x) % P;
		fA += ff[c ^ 1][s] * x;
	}
	for(int i = 1;i < n; ++i) iv = 1LL * iv * i % P, fA /= i;
	printf("%d\n%d\n", (int)(fA + 0.5), 1LL * A * inv(iv) % P);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章