1. 程式人生 > >LibreOJ #2547.「JSOI2018」防禦網絡 動態規劃

LibreOJ #2547.「JSOI2018」防禦網絡 動態規劃

題意

給出一棵點仙人掌,問點集V的所有子集的斯坦納樹大小的期望是多少。取模。
n200

分析

考慮把每個點雙的貢獻分開算。對於一條割邊,它在斯坦納樹中出現,當且僅當被它連線的兩個連通塊中都有點被選擇。
對於一個環,設有x個點的子樹中有點被選,則這x個點必須保證連通。那麼我們肯定是找到距離最大的一對相鄰點,然後把它們中間的邊斷開。
考慮列舉這個距離,設gi表示有多少種選點方案滿足任意一對相鄰點之間的距離不小於i,那麼相鄰點之間距離的最大值為L的方案就是gLgL1
考慮如何求gi,列舉環上編號最小的被選擇點,然後從這個點開始dp,設fi

表示i前面的點已處理完,且i一定被選的方案,用字首和來優化轉移即可。
時間複雜度是O(n3)

程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>

typedef long long LL;

const int N=405;
const int MOD=1000000007;

int n,m,cnt,last[N],dep[N],fa[N],tot,bel[N],ans,a[N],b[N],size[N],f[N],g[N],bin[N],s
[N]; bool vis[N]; struct edge{int to,next;}e[N*2]; std::vector<int> vec[N]; int ksm(int x,int y) { int ans=1; while (y) { if (y&1) ans=(LL)ans*x%MOD; x=(LL)x*x%MOD;y>>=1; } return ans; } void addedge(int u,int v) { e[++cnt].to=v;e[cnt].next=last
[u];last[u]=cnt; e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt; } void pre(int x) { vis[x]=1;dep[x]=dep[fa[x]]+1;size[x]=1; for (int i=last[x];i;i=e[i].next) { if (e[i].to==fa[x]) continue; if (!vis[e[i].to]) fa[e[i].to]=x,pre(e[i].to),size[x]+=size[e[i].to]; else if (dep[e[i].to]<dep[x]) { tot++;vec[tot].push_back(x);bel[x]=tot; for (int j=x;j!=e[i].to;j=fa[j]) vec[tot].push_back(fa[j]),bel[fa[j]]=tot; } } } int main() { scanf("%d%d",&n,&m); bin[0]=1; for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2%MOD; for (int i=1;i<=m;i++) { int x,y;scanf("%d%d",&x,&y); addedge(x,y); } pre(1); for (int i=1;i<=cnt;i+=2) { int x=e[i].to,y=e[i+1].to; if (dep[x]>dep[y]) std::swap(x,y); if (bel[x]!=bel[y]||!(bel[x]*bel[y])) (ans+=(LL)(bin[size[y]]-1)*(bin[n-size[y]]-1)%MOD)%=MOD; } for (int i=1;i<=tot;i++) { int sz=0,L=vec[i].size(); for (int j=0;j<vec[i].size();j++) a[++sz]=vec[i][j]; for (int j=0;j<vec[i].size();j++) a[++sz]=vec[i][j]; for (int j=1;j<=sz;j++) { b[j]=1; for (int k=last[a[j]];k;k=e[k].next) if (bel[e[k].to]!=bel[a[j]]) b[j]+=e[k].to==fa[a[j]]?n-size[a[j]]:size[e[k].to]; } memset(g,0,sizeof(g)); for (int j=1;j<=L;j++) { for (int len=1;len<=L;len++) { memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); f[j]=s[j]=1; for (int k=j+1;k<=j+L;k++) if ((k-1)%L+1>=j) f[k]=(LL)(s[k-1]+MOD-s[std::max(k-len-1,0)])*(bin[b[k]]-1)%MOD,s[k]=(s[k-1]+f[k])%MOD; else f[k]=0,s[k]=s[k-1]; (g[len]+=f[j+L])%=MOD; } } for (int j=1;j<=L;j++) (ans+=(LL)(L-j)*(g[j]+MOD-g[j-1])%MOD)%=MOD; } printf("%d",(LL)ans*ksm(ksm(2,MOD-2),n)%MOD); return 0; }