解题思路:
注意到该图是一棵树上多了几个不相交的环。
考虑每条边的贡献。
对于一条树边(桥),只要两边有点那就要选。
对于一个环,其贡献的长度就是环长减去最长的空段,可以枚举最长空隙和起点dp,详见代码。
时间复杂度 O(n3) O ( n 3 )
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=205,mod=1e9+7;
int n,m,dep[N],size[N],fa[N],mark[N];
vector<int>g[N];
ll ans,bin[N],a[N],dp[N][2],sum[N][2];
ll Pow(ll x,ll y)
{
ll res=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1)res=res*x%mod;
return res;
}
void calc(int st,int len)
{
for(int i=st-1;i<=m;i++)dp[i][0]=dp[i][1]=sum[i][0]=sum[i][1]=0;
dp[st][0]=sum[st][0]=a[st];
for(int i=st+1;i<=m;i++)
{
if(i-len>=st)dp[i][1]=(dp[i][1]+a[i]*(dp[i-len][0]+dp[i-len][1]))%mod;
int l=max(st,i-len+1)-1;
dp[i][0]=(dp[i][0]+a[i]*(sum[i-1][0]+mod-sum[l][0]))%mod,dp[i][1]=(dp[i][1]+a[i]*(sum[i-1][1]+mod-sum[l][1]))%mod;
sum[i][0]=(sum[i-1][0]+dp[i][0])%mod;
sum[i][1]=(sum[i-1][1]+dp[i][1])%mod;
ans=(ans+min(i-st,m-len)*dp[i][1])%mod;
}
}
void dfs(int x)
{
size[x]=1;
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];if(y==fa[x])continue;
if(dep[y])
{
if(dep[y]>dep[x])
{
m=0;int last=0;
for(int k=y;k!=x;last=k,k=fa[k])
a[++m]=bin[size[k]-size[last]],mark[k]=1;
a[++m]=bin[n-size[last]];
for(int len=1;len<m;len++)
for(int st=1;st<=m-len;st++)
calc(st,len);
}
continue;
}
fa[y]=x,dep[y]=dep[x]+1;
dfs(y),size[x]+=size[y];
}
}
int main()
{
//freopen("defense.in","r",stdin);
//freopen("defense.out","w",stdout);
n=getint(),m=getint();
bin[0]=1;
for(int i=1;i<=n;i++)bin[i]=bin[i-1]*2%mod;
for(int i=1;i<=n;i++)bin[i]--;
while(m--)
{
int x=getint(),y=getint();
g[x].push_back(y),g[y].push_back(x);
}
dep[1]=1,dfs(1);
for(int i=2;i<=n;i++)if(!mark[i])ans=(ans+bin[size[i]]*bin[n-size[i]])%mod;
ans=ans*Pow(bin[n]+1,mod-2)%mod;
cout<<ans<<'\n';
return 0;
}

本文介绍了一种解决特殊图结构问题的方法,针对一棵树加上若干不相交环的情况,通过树形DP技术和环上的动态规划来求解最小路径覆盖问题,详细解析了算法实现,并给出了具体的时间复杂度。
638

被折叠的 条评论
为什么被折叠?



