洛谷P4517 [JSOI2018]防御网络(dp)

题面

传送门

题解

翻译一下题意就是每次选出一些点,要用最少的边把这些点连起来,求期望边数

我也不知道为什么反正总之就是暴力枚举太麻烦了所以我们考虑贡献

如果一条边是割边,那么它会在图里当且仅当两边的联通块中都有点被选,设其中一边的点的个数为\(siz\),那么方案数就是\((2^{siz}-1)(2^{n-siz}-1)\)

然而如果一条边是环边就会变得非常麻烦了……每种方案相当于这个环上有若干个点被标记,要用最少的边数将它们连起来,那么边数就是环的大小减去\(\max(\)两个相邻点之间的边数\()\)

这个不能分开考虑了,我们把整个环给一起考虑

\(dp_{i,j,k}\)表示选择的点中左端点为\(i\),右端点为\(j\),不考虑\(j->n->i\)这条路径的情况下的两两相邻点之间最大值为\(k\)的情况,设环长为\(n\),那么这个就会产生\((n-\max(k,n+i-j))\times dp_{i,j,k}\)的贡献

转移方程为

\[dp_{i,j,k}=(2^{siz_j}-1)\left(\sum_{p=0}^kdp_{i,j-k,p}+\sum_{p=j-k+1}^{j-1}dp_{i,p,k}\right) \]

上面的方程的意思就是,如果\(j\)和前面一个点的长度恰好为\(k\),那么前面最大长度可以任取,否则前面最大长度就必须等于\(k\)

发现转移方程里的两个柿子都可以写成前缀和的形式,那么我们一边计算一边维护前缀和,总的复杂度就是\(O(n^3)\)

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1005,P=1e9+7;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
	return res;
}
struct eg{int v,nx,w,f;}e[N<<1];int head[N],tot=1;
inline void add_edge(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int dfn[N],low[N],sz[N],vis[N],sum1[N][N],sum2[N][N],st[N],val[N];
int n,m,tim,res,top,tmp,u,v;
void tarjan(int u,int fa){
	low[u]=dfn[u]=++tim,sz[u]=1;
	go(u)if(v!=fa){
		if(!dfn[v])tarjan(v,u),cmin(low[u],low[v]),sz[u]+=sz[v];
		else cmin(low[u],dfn[v]);
		if(low[v]>dfn[u]){
			e[i].f=e[i^1].f=1,res=add(res,mul(ksm(2,sz[v])-1,ksm(2,n-sz[v])-1));
			e[i].w=sz[v],e[i^1].w=n-sz[v];
		}
	}
}
inline int nxt(R int u){go(u)if(!e[i].f&&!vis[v])return v;return 0;}
void solve(int p){
	top=0;while(p)st[++top]=p,vis[p]=1,p=nxt(p);
	if(top==1)return;
	fp(i,1,top)val[i]=1;
	fp(u,1,top)go(st[u])val[u]+=e[i].w;
	fp(i,1,top)val[i]=ksm(2,val[i])-1;
	fp(i,1,top-1){
		fp(j,0,top)sum2[i][j]=val[i];
		fp(j,i+1,top)fp(k,1,top){
			tmp=0;
			if(j-i>=k){
				tmp=mul(val[j],add(sum2[j-k][k],dec(sum1[j-1][k],sum1[j-k][k])));
				res=add(res,mul(tmp,top-max(top+i-j,k)));
			}
			sum1[j][k]=add(sum1[j-1][k],tmp),sum2[j][k]=add(sum2[j][k-1],tmp);
		}
		fp(j,i,top)fp(k,0,top)sum1[j][k]=sum2[j][k]=0;
	}
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),m=read();
	fp(i,1,m)u=read(),v=read(),add_edge(u,v),add_edge(v,u);
	tarjan(1,0);
	fp(i,1,n)if(!vis[i])solve(i);
	printf("%d\n",mul(res,ksm(ksm(2,n),P-2)));
	return 0;
}
posted @ 2019-03-11 17:48  bztMinamoto  阅读(138)  评论(0编辑  收藏  举报
Live2D