1. 程式人生 > >BZOJ5315 [JSOI2018]防禦網絡 【仙人掌 + dp】

BZOJ5315 [JSOI2018]防禦網絡 【仙人掌 + dp】

pair ++ CM https spa geo 表示 size ||

題目鏈接

BZOJ5315

題解

題目好嚇人= =點仙人掌 + 斯坦納樹

我們只需求出對於所有選點的方案的斯坦納樹邊長總和
\(n\)那麽大當然不能狀壓,但是考慮一下如果這是一棵樹,一個方案的貢獻就是連接這些點的所有邊
我們可以考慮計算每條邊的貢獻
一條邊在樹上有貢獻,當且僅當它兩端的樹都存在被選擇的點
那麽這條邊\((u,v)\)貢獻就是
\[(2^{siz[u]} - 1)(2^{siz[v] - 1})\]
其中\(siz[u]\)表示斷開這條邊後\(u\)一側的樹大小

如果放到仙人掌上呢?
對於割邊,和樹是一樣的
我們只需計算每個環的貢獻
考慮我們對於一個環,選擇了其中\(K\)個點所在外向樹,那麽就有連接\(K\)

個點的環上的\(K\)段邊,我們一定是除去最長那一條
所以我們斷環為鏈,設\(f[i][j][k]\)為選擇了區間\([i,j]\)的外向樹【意味著端點必選,中間不一定選,區間外一定不選】,\([i,j]\)中最大距離為\(k\)的方案數
那麽有,即考慮最後一段的長度
\[f[i][j][k] = (2^{siz[j]} - 1)(\sum\limits_{x = 0}^{k}f[i][j - k][x] + \sum\limits_{x = j - k + 1}^{j - 1}f[i][x][k])\]

直接轉移是\(O(n^4)\)的,常數很小數據很水可以跑過。。。

當然可以前綴和優化成\(O(n^3)\)


【其實是我前綴和寫炸了,直接交一波暴力轉移竟然\(A\)了。。。】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 205,maxm = 100005,INF = 1000000000,P = 1000000007; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int h[maxn],ne = 1; struct EDGE{int to,nxt;}ed[maxm]; inline void build(int u,int v){ ed[++ne] = (EDGE){v,h[u]}; h[u] = ne; ed[++ne] = (EDGE){u,h[v]}; h[v] = ne; } int n,m; inline int qpow(int a,int b){ int re = 1; for (; b; b >>= 1,a = 1ll * a * a % P) if (b & 1) re = 1ll * re * a % P; return re; } int dfn[maxn],low[maxn],siz[maxn],fa[maxn],cnt,sum; int v[maxn],K; LL f[maxn][maxn][maxn],D[maxn][maxn][maxn],S[maxn][maxn][maxn],ans,bin[maxn]; void DP(int rt,int u){ K = 0; int tot = 0; for (int i = u; i != rt; i = fa[i]){ v[++K] = siz[i]; siz[rt] += siz[i]; tot += siz[i]; } v[++K] = sum - tot; cls(f); for (int l = 1; l <= K; l++) for (int r = l; r <= K; r++) for (int k = 0; k <= r - l; k++){ if (l == r){ if (k == 0) f[l][r][k] = bin[v[l]] - 1; continue; } int d = 0,s = 0; for (int i = 0; i <= k; i++) d = (d + f[l][r - k][i]) % P; for (int i = r - k + 1; i < r; i++) s = (s + f[l][i][k]) % P; f[l][r][k] = (bin[v[r]] - 1) * (d + s) % P; ans = (ans + 1ll * (K - max(K - r + l,k)) * f[l][r][k] % P) % P; } } void dfs(int u){ dfn[u] = low[u] = ++cnt; siz[u] = 1; Redge(u) if ((to = ed[k].to) != fa[u]){ if (!dfn[to]){ fa[to] = u; dfs(to); low[u] = min(low[u],low[to]); } else low[u] = min(low[u],dfn[to]); if (low[to] > dfn[u]){ ans = (ans + 1ll * (bin[siz[to]] - 1) * (bin[sum - siz[to]] - 1) % P) % P; siz[u] += siz[to]; } } Redge(u) if (fa[to = ed[k].to] != u && dfn[u] < dfn[to]) DP(u,to); } int main(){ bin[0] = 1; for (int i = 1; i <= 200; i++) bin[i] = bin[i - 1] * 2ll % P; n = read(); m = read(); while (m--) build(read(),read()); sum = n; dfs(1); ans = ans * qpow(bin[n],P - 2) % P; printf("%lld\n",ans); return 0; }

BZOJ5315 [JSOI2018]防禦網絡 【仙人掌 + dp】