【BZOJ5315】[JSOI2018]防御网络(动态规划,仙人掌)
【BZOJ5315】[JSOI2018]防御网络(动态规划,仙人掌)
题面
题解
显然图是仙人掌。
题目给了斯坦纳树就肯定不是斯坦纳树了,,,,
总不可能真让你\(2^n\)枚举点集再来一个至少\(2^n*n\)的斯坦纳树吧。。。
现在对于每一条边考虑贡献。
如果这条边是不在环内,那么这条边被选当且仅当其子树内外都有点备选,这个随便算算就知道贡献了。
然后就是环上的边,只考虑这个环,如果一个点的子树内选择了点的话就把环上这个点给标记出来,那么最后选择的东西就一定是整个环的长度减去相邻两个被选中的点的最大距离。
那么这样子就可以把每个环单独拎出来考虑这个环上的所有边的答案。
枚举每一个最大的可删去的长度,设\(f_x\)表示任意一对选择的点的最大长度不超过\(x\)的方案数。
那么对于\(x\)而言,方案数就是\(f_x-f_{x-1}\)。
因为是一个环,断环成链后枚举链上最靠左的被选择的点,这样子可以忽略断开后首尾直接的关系。
剩下的部分直接\(dp\),设\(g_i\)表示当前选择了第\(i\)个点,并且第\(i\)个点强制被选择的方案数,转移的时候强迫相邻点的距离不超过枚举的长度\(x\),这个东西可以用前缀和优化。
那么枚举长度、枚举左端点再\(dp\),所以这部分的复杂度是\(O(n^3)\)的。
#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;
}
【BZOJ5315】[JSOI2018]防御网络(动态规划,仙人掌)的更多相关文章
- BZOJ5315 [JSOI2018]防御网络  【仙人掌 + dp】
		题目链接 BZOJ5315 题解 题目好吓人= =点仙人掌 + 斯坦纳树 我们只需求出对于所有选点的方案的斯坦纳树边长总和 \(n\)那么大当然不能状压,但是考虑一下如果这是一棵树,一个方案的贡献就是 ... 
- bzoj5315/luoguP4517 [JSOI2018]防御网络(仙人掌,dp)
		bzoj5315/luoguP4517 防御网络(仙人掌,dp) bzoj Luogu 题目描述略(太长了) 题解时间 本题和斯坦纳树无关. 题面保证了是一个仙人掌...? 但这个环之间甚至交点都没有 ... 
- bzoj 5315: [Jsoi2018]防御网络
		Description Solution 考虑每一条边的贡献 对于树边,如果两边各存在一个点,那么有贡献,总贡献就是 \((2^{size}-1)*(2^{n-size}-1)\) 分别对应两边的 \ ... 
- 洛谷P4517 [JSOI2018]防御网络(dp)
		题面 传送门 题解 翻译一下题意就是每次选出一些点,要用最少的边把这些点连起来,求期望边数 我也不知道为什么反正总之就是暴力枚举太麻烦了所以我们考虑贡献 如果一条边是割边,那么它会在图里当且仅当两边的 ... 
- LOJ #2547 Luogu P4517「JSOI2018」防御网络
		好像也没那么难写 LOJ #2547 Luogu P4517 题意 在一棵点仙人掌中等概率选择一个点集 求选出点集的斯坦纳树大小的期望 定义点仙人掌为不存在一个点在多个简单环中的连通图 斯坦纳树为在原 ... 
- LOJ 2547 「JSOI2018」防御网络——思路+环DP
		题目:https://loj.ac/problem/2547 一条树边 cr->v 会被计算 ( n-siz[v] ) * siz[v] 次.一条环边会被计算几次呢?于是去写了斯坦纳树. #in ... 
- 【LOJ】 #2547. 「JSOI2018」防御网络
		题解 如果只是一棵树的话,那么就枚举每条边,分成两部分大小为\(a\)和\(b\) 那么这条边被统计的方案数是\((2^a - 1)(2^b - 1)\) 如果是一个环的话,我们枚举环上至少有\(N ... 
- yyb省选前的一些计划
		突然意识到有一些题目的计划,才可以减少大量查水表或者找题目的时间. 所以我决定这样子处理. 按照这个链接慢慢做. 当然不可能只做省选题了. 需要适时候夹杂一些其他的题目. 比如\(agc/arc/cf ... 
- JSOI2018简要题解
		来自FallDream的博客,未经允许,请勿转载,谢谢. 有幸拜读到贵省的题目,题的质量还不错,而且相比zjoi可做多了,简单发一下题解吧. 还有就是,怎么markdown在博客园上的代码这么丑啊 「 ... 
随机推荐
- sql中distinct和order by问题的解决方案
			需求:根据PID字段对数据去重,根据Sort字段排序,需要显示这个两个字段. 如图,这是原始数据,先排序: 排序后发现两个项是重复的,需要去除一个, 因为Distinct对检查Select里面的每一列 ... 
- C#设计模式之7:适配器模式
			适配器模式 使用适配器模式的一个重要的点是首先要识别出什么代码(接口)是已经存在的,什么代码(接口)是新的,需要去适配的.适配器的作用是让旧的(现有的)接口能够匹配新的系统(要去适配的). 比如有下面 ... 
- Day 6-3 粘包现象
			服务端: import socket import subprocess phone = socket.socket(family=socket.AF_INET, type=socket.SOCK_S ... 
- Day 5-2 类的继承和派生,重用
			类的继承 派生 在子类中重用父类 组合 抽象类 定义: 继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创建新类的方式,在python中,新 ... 
- scala mkstring
			如果你想要把集合元素转化为字符串,可能还会添加分隔符,前缀,后缀. Solution 使用mkString方法来打印一个集合内容,下面给一个简单的例子: scala> val a = Array ... 
- python学习笔记(7)--循环语句
			循环语句如下: for i in range(start, end): //注意 前闭后开 coding for i in range(m,n,k): coding for c in s: codin ... 
- cmd & tree & bash
			cmd & tree & bash bug E: Unable to locate package tree solution # 1. update $ sudo apt-get u ... 
- easyui 自动动态合并单元格
			.......onLoadSuccess : function(data) { if (data.rows.length > 0) { //调用mergeCellsByField()合并单元格 ... 
- C#使用MemoryStream类读写内存
			MemoryStream和BufferedStream都派生自基类Stream,因此它们有很多共同的属性和方法,但是每一个类都有自己独特的用法.这两个类都是实现对内存进行数据读写的功能,而不是对持久性 ... 
- 阿里云 ECS 安全组
			以前在案例云买的ECS我一般都是 连上 ssh,然后把网站文件拿上去 ,安装好需要的环境 然后就可以顺利的打开网站了,这次帮一个朋友买的阿里云ECS让我蒙了, 一切都准备好了 网站打不开 防火墙也检查 ... 
