2021牛客国庆集训派对day1 G - Steiner Tree

Description

给你一个n个点m条边的图,求不同的斯坦纳树的个数,

1 ≤ n ≤ 50 , K ( 关 键 点 ) ≤ 12 1\leq n \leq 50,K(关键点)\leq 12 1n50K()12

Solution

老年人复习题,

将DP分成两类:f,g,分别表示 限制一定为链的 和 无限制的 答案,

如何不重不漏枚举子集:
强制选最高位为f,剩下的为g,这样就OK了

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define sqr(q) ((q)*(q))
using namespace std;
typedef long long LL;
typedef double db;
typedef pair<int,int> pii;
const int N=60,M=8200,mo=1e9+7,INF=mo+1;
int read(int &n)
{
	bool q=0;n=0;char ch=' ';
	for(;ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
	if(ch=='-')ch=getchar(),q=1;
	for(;ch>='0'&&ch<='9';ch=getchar())n=(n<<3)+(n<<1)+ch-48;
	return q?n=-n:n;
}
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
mt19937 rand_num(seed);  // 大随机数
uniform_int_distribution<int> dist(0, 1000000000);
int RD(int q){return rand_num()%q;}


int n,m,K;
vector<int>B[N];
int zx[N];
int er[N];
void link(int q,int w)
{
	B[q].push_back(w);
	B[w].push_back(q);
}
struct Val
{
	int v;
	LL cnt;
	Val(int q=INF,LL w=0):v(q),cnt(w){}
	void MIN(Val q)
	{
		if(q.v>v)return;
		if(q.v<v)
		{
			v=q.v;
			cnt=q.cnt;
		}else cnt=(cnt+q.cnt)%mo;
	}
	friend Val operator+(Val q,Val w){return Val(q.v+w.v,q.cnt*w.cnt%mo);}
}f[N][M],g[N][M];

Val dis[N];
vector<int>d[N];

void BFS()
{
	fo(i,1,n)if(dis[i].cnt && dis[i].v<n)
	{
		d[dis[i].v].push_back(i);
	}
	
	fo(I,1,n)
	{
		for(int q:d[I])if(dis[q].v==I)
		{
			for(int w:B[q])
			{
				if(dis[w].v>I+1)d[I+1].push_back(w);
				dis[w].MIN(dis[q]+Val(1,1));
			}
		}
		d[I].clear();
	}
}

void steiner()
{
	fo(i,1,n)
	{
		fo(j,0,er[K+1]-1)f[i][j]=g[i][j]=Val();
		g[i][0]=Val(0,1);
	}
	fo(I,1,er[K+1]-1)
	{
		fo(i,1,n)dis[i]=Val();
		fo(i,1,n)
		{
			if(er[zx[i]]&I)
			{
				g[i][I]=g[i][I^er[zx[i]]];
			}else
			{
				for(int j=I;j>I-j;j=(j-1)&I)
				{
					g[i][I].MIN(f[i][j]+g[i][(I-j)]);
				}
			}

			for(int w:B[i])
				dis[w].MIN(g[i][I]+Val(1,1));
		}
		BFS();
		fo(i,1,n)f[i][I].MIN(dis[i]),g[i][I].MIN(f[i][I]);
	}
}

int main()
{
	int q,w,e;
	er[1]=1;fo(i,2,15)er[i]=er[i-1]<<1;
	while(scanf("%d%d%d",&n,&m,&K)!=EOF)
	{
		fo(i,1,m)read(q),read(w),link(q,w);
		fo(i,1,K)zx[i]=i;
		fo(i,K+1,n)zx[i]=0;

		steiner();

		printf("%d ",g[1][er[K+1]-1].v);
		printf("%lld\n",g[1][er[K+1]-1].cnt);

		fo(i,1,n)B[i].clear(),zx[i]=0;
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值