【BZOJ5315】[JSOI2018]防護網絡(動態規劃,仙人掌)

【BZOJ5315】[JSOI2018]防護網絡(動態規劃,仙人掌)

題面

BZOJ 洛谷php

題解

顯然圖是仙人掌。 題目給了斯坦納樹就確定不是斯坦納樹了,,,, 總不可能真讓你$2^n$枚舉點集再來一個至少$2^n*n$的斯坦納樹吧。。。 如今對於每一條邊考慮貢獻。 若是這條邊是不在環內,那麼這條邊被選當且僅當其子樹內外都有點備選,這個隨便算算就知道貢獻了。 而後就是環上的邊,只考慮這個環,若是一個點的子樹內選擇了點的話就把環上這個點給標記出來,那麼最後選擇的東西就必定是整個環的長度減去相鄰兩個被選中的點的最大距離。 那麼這樣子就能夠把每一個環單獨拎出來考慮這個環上的全部邊的答案。 枚舉每個最大的可刪去的長度,設$f_x$表示任意一對選擇的點的最大長度不超過$x$的方案數。 那麼對於$x$而言,方案數就是$f_x-f_{x-1}$。 由於是一個環,斷環成鏈後枚舉鏈上最靠左的被選擇的點,這樣子能夠忽略斷開後首尾直接的關係。 剩下的部分直接$dp$,設$g_i$表示當前選擇了第$i$個點,而且第$i$個點強制被選擇的方案數,轉移的時候強迫相鄰點的距離不超過枚舉的長度$x$,這個東西能夠用前綴和優化。 那麼枚舉長度、枚舉左端點再$dp$,因此這部分的複雜度是$O(n^3)$的。ios

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 205
#define MOD 1000000007
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
struct Line{int v,next;}e[MAX<<2];
int h[MAX],cnt=2;bool vis[MAX<<2];
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,m,size[MAX],ans,bin[MAX],fr[MAX],fa[MAX];
vector<int> dn,up;
int S[MAX],top,sz[MAX],dep[MAX];
void Get(int u,int R)
{
	S[top=1]=u;
	for(int j=u;j!=R;j=fa[j])
		vis[fr[j]>>1]=true,S[++top]=fa[j];
}
void dfs(int u,int ff)
{
	size[u]=1;fa[u]=ff;dep[u]=dep[ff]+1;int R=0;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		if(size[v]){if(dep[v]<dep[u])Get(u,R=v);
			vis[i>>1]=true;continue;}
		fr[v]=i;dfs(v,u);size[u]+=size[v];
	}
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		if(vis[i>>1])continue;
		ans=(ans+1ll*(bin[size[v]]-1)*(bin[n-size[v]]-1))%MOD;
	}
	if(R)dn.push_back(u),up.push_back(R);
}
int g[MAX],f[MAX],ss[MAX];
void Solve(int u,int R)
{
	Get(u,R);
	for(int i=1;i<=top;++i)sz[i]=size[S[i]];
	for(int i=top;i>1;--i)sz[i]-=sz[i-1];
	sz[top]+=n-size[S[top]];
	for(int i=1;i<=top;++i)f[i]=0;
	for(int l=1;l<=top;++l)
	{
		for(int i=1;i<=l;++i)
		{
			for(int j=1;j<=top;++j)ss[j]=g[j]=0;
			g[i]=ss[i]=bin[sz[i]]-1;
			for(int j=i+1;j<=top;++j)
			{
				int L=max(1,j-l),R=j-1,d=bin[sz[j]]-1;
				g[j]=1ll*d*(ss[R]-ss[L-1]+MOD)%MOD;
				ss[j]=(ss[j-1]+g[j])%MOD;
			}
			int L=max(top-l+i,i+1);
			f[l]=(1ll*f[l]+ss[top]-ss[L-1]+MOD)%MOD;
		}
	}
	for(int i=1;i<=top;++i)
		ans=(ans+1ll*(top-i)*(f[i]+MOD-f[i-1]))%MOD;
	return;
}
int main()
{
	n=read();m=read();
	bin[0]=1;for(int i=1;i<=n;++i)bin[i]=(bin[i-1]<<1)%MOD;
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read();
		Add(u,v);Add(v,u);
	}
	dfs(1,0);
	for(int i=0;i<dn.size();++i)Solve(dn[i],up[i]);
	ans=1ll*ans*fpow(bin[n],MOD-2)%MOD;
	printf("%d\n",ans);
	return 0;
}
相關文章
相關標籤/搜索