[JLOI2015]管道连接

题目传送门

分析:
同色点连通挺恶心的
要求全部点连通的话就可以直接斯坦纳树了
同色点连通满足还要考虑异色点可能会共用边使答案更小
于是尝试枚举一个颜色集合的点全部连通,形成斯坦纳树
然后把所有集合拼起来,形成斯坦纳森林
之中有一种方案一定是最小的
枚举子集的子集的子集复杂度是\(O(4^n)\)
总复杂度为\(O(n4^p)\)
开O2跑得过还真离谱

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<map>

#define maxn 1005
#define maxm 6005
#define INF 0x3f3f3f3f

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,K,m;
int fir[maxn],nxt[maxm],to[maxm],len[maxm],cnt;
long long f[maxn][1<<10],g[1<<10];
bool vis[maxn];
map<int,int>M;
int cur;
int c[maxn],id[maxn];

struct node{
	int u;long long w;
	friend bool operator <(node x,node y){return x.w>y.w;}
};
priority_queue<node>Q;

inline void newnode(int u,int v,int w)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,len[cnt]=w;}
inline void dij(int S)
{
	memset(vis,0,sizeof vis);
	while(!Q.empty())
	{
		int u=Q.top().u;Q.pop();
		if(vis[u])continue;vis[u]=1;
		for(int i=fir[u];i;i=nxt[i])
			if(f[to[i]][S]>f[u][S]+len[i])
				Q.push((node){to[i],f[to[i]][S]=f[u][S]+len[i]});
	}
}

inline long long solve(int t)
{
	for(int i=0;i<(1<<t);i++)
	{
		for(int k=1;k<=n;k++)
		{
			for(int j=i&(i-1);j;j=i&(j-1))
				f[k][i]=min(f[k][i],f[k][j]+f[k][i^j]);
			if(f[k][i]!=f[0][0])Q.push((node){k,f[k][i]});
		}
		dij(i);
	}
	long long ans=1ll<<60;
	for(int i=1;i<=n;i++)ans=min(ans,f[i][(1<<t)-1]);
	return ans;
}

int main()
{
	n=getint(),m=getint(),K=getint();
	memset(f,INF,sizeof f);
	for(int i=1;i<=m;i++)
	{
		int u=getint(),v=getint(),w=getint();
		newnode(u,v,w),newnode(v,u,w);
	}
	for(int i=1;i<=K;i++)
	{
		c[i]=getint(),id[i]=getint();
		if(!M[c[i]])M[c[i]]=++cur;
		c[i]=M[c[i]];
	}
	memset(g,INF,sizeof g);
	for(int i=1;i<(1<<cur);i++)
	{
		int tmp=0;
		memset(f,INF,sizeof f);
		for(int j=1;j<=K;j++)if(i&(1<<(c[j]-1)))f[id[j]][1<<tmp]=0,tmp++;
		g[i]=solve(tmp);
	}
	for(int i=1;i<(1<<cur);i++)for(int j=i&(i-1);j;j=i&(j-1))
		g[i]=min(g[i],g[j]+g[i^j]);
	printf("%lld\n",g[(1<<cur)-1]);
}

posted @ 2020-06-10 22:25  Izayoi_Doyo  阅读(174)  评论(0编辑  收藏  举报