[bzoj 4774]修路

传送门

Description 

该题出现在了FCS省选模拟赛的D4T1

可以把最后的图分成若干个不相交的部分,显然每个部分都是包含的指定点集合的一个斯坦纳树

枚举子集合并即可


Code 

/*
	斯坦纳树;O(n*3^n+kE*2^n) 暂且把O(k*E)当成是spfa的复杂度
	15:15~16:20
*/
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
const int MN=10005,MM=20005,inf=0x3f3f3f3f;
int n,m,k;
struct edge{int to,w,nex;}e[MM<<1];int hr[MN],en;
inline void ins(int f,int t,int w)
{
	e[++en]=(edge){t,w,hr[f]};hr[f]=en;
	e[++en]=(edge){f,w,hr[t]};hr[t]=en;
}
std::queue<int> q;
int f[1<<10][MN],g[1<<10],refer[1<<5],ans=inf;
bool inq[MN];
void spfa(int *d)
{
	register int u,i;
	while(!q.empty())
	{
		u=q.front();q.pop();inq[u]=false;
		for(i=hr[u];i;i=e[i].nex)
			if(d[e[i].to]>d[u]+e[i].w)
			{
				d[e[i].to]=d[u]+e[i].w;
				if(!inq[e[i].to]) q.push(e[i].to),inq[e[i].to]=true;
			}
	}
}
int get(int x)
{
	if(x<=k/2) return x*2-1;
	if(x+k/2>n) return (n-x+1)*2;
	return x+k/2;
}
int main()
{
	register int i,j,x,y,S,SS,SSS,s;
	n=read();m=read();k=read()<<1;SS=1<<k;SSS=1<<(k>>1);
	while(m--)x=get(read()),y=get(read()),ins(x,y,read());
	memset(f,0x3f,sizeof f);
	for(i=1;i<=k;++i) f[1<<i-1][i]=0;
	for(S=1;S<SS;++S)
	{
		for(i=1;i<=n;++i)
		{
			for(s=S&(S-1);s;s=(s-1)&S) f[S][i]=min(f[S][i],f[s][i]+f[S^s][i]);
			if(f[S][i]<inf) q.push(i);
		}
		spfa(f[S]);g[S]=inf;
		for(i=1;i<=n;++i) g[S]=min(g[S],f[S][i]);
	}
	for(i=0;i<SSS;++i)
	{
		s=0;
		for(j=0;j<k/2;++j) if(i>>j&1) s|=1<<(j*2);
		refer[i]=s|(s<<1);
	}
	for(S=1;S<SSS;++S)for(s=S&(S-1);s;s=(s-1)&S)
		g[refer[S]]=min(g[refer[S]],g[refer[s]]+g[refer[S^s]]);
	printf("%d
",g[SS-1]==inf?-1:g[SS-1]);
	return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/bzoj_4774.html