CF1149D Abandoning Roads

一、题目

点此看题

二、解法

套路:当只有两个关键状态量时,我们以一个量为主,一个量为辅思考问题。

那么我们以 (a) 边为主,因为不可能表示出原图的最小生成树所以我们开始找结论。根据 ( t kruskall) 算法我们先把所有 (a) 边连起来,那么会形成若干个 (a) 边连通块,考虑 (b) 边会把这些连通块串成一棵生成树。

结论:(a ightarrow b) 的路径可能在最小生成树中出现,当且仅当这条路径没有通过 (b) 边走出 (a) 连通块再通过 (b) 边走回来,并且不经过 (a) 连通块内的 (b),因为走出去和走回来的两条 (b) 边是不可能同时出现的,而 (a) 连通块内的 (b) 边无论如何都不在最小生成树中。但 (a) 连通块内部的 (a) 边路径和经过 (b) 边走到其他连通块的路径都是合法的,证毕。

知道这个结论以后就得到了一个最短路问题,由于限制有不能走回以前的连通块,所以我们记录已经离开的连通块有哪些。设 (dp[s][u])离开过的连通块集合(s),现在所处的点为 (u),然后跑 ( t dijkstra) 即可。

但是 (s) 可能很大,考虑大小 (leq 3) 的连通块是不需要记录在 (s) 中的,因为最优决策下绝不会出现通过 (b) 边走出去再走回来的情况,那么时间复杂度 (O(2^{n/4}cdot mcdot log))

三、总结

首先是开头那个很重要的套路。

此外猜结论要对着要求的东西猜,比如这道题要求的是最短路就找最短路合法的条件。

最后是有关连通块的状压,可以讨论一些较小的连通块可以大幅度降低复杂度。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int M = 75;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a,b,tot,cnt,tmp,id[M],vis[M],f[M],dp[1<<17][M];
struct edge
{
	int v,c,next;
}e[M*M];
struct node
{
	int s,u,c;
	bool operator < (const node &b) const
	{
		return c>b.c;
	}
};
void dfs(int u,int c)
{
	id[u]=c;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(id[v]==-1 && e[i].c==a) dfs(v,c);
	}
}
int cal(int u)
{
	vis[u]=1;int r=1;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(!vis[v] && e[i].c==a) r+=cal(v);
	}
	return r;
}
void dijk()
{
	memset(dp,0x3f,sizeof dp);
	priority_queue<node> q;
	q.push(node{0,1,0});dp[0][1]=0;
	while(!q.empty())
	{
		int s=q.top().s,u=q.top().u,c=q.top().c;
		q.pop();
		if(c>dp[s][u]) continue;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,ts=s;
			//leave the block before
			if(id[v]<tmp && (s&(1<<id[v]))) continue;
			//in the same block but use b
			if(id[u]==id[v] && e[i].c==b) continue;
			//enter a brand new block
			if(id[u]<tmp && id[u]!=id[v]) ts|=(1<<id[u]);
			if(dp[ts][v]>dp[s][u]+e[i].c)
			{
				dp[ts][v]=dp[s][u]+e[i].c;
				q.push(node{ts,v,dp[ts][v]});
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		int ans=1e9;
		for(int j=0;j<(1<<tmp);j++)
			ans=min(ans,dp[j][i]);
		printf("%d ",ans);
	}
}
signed main()
{
	n=read();m=read();a=read();b=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge{v,c,f[u]},f[u]=tot;
		e[++tot]=edge{u,c,f[v]},f[v]=tot;
	}
	memset(id,-1,sizeof id);
	for(int i=1;i<=n;i++)
		if(!vis[i] && cal(i)>=4) dfs(i,cnt++);
	tmp=cnt;
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++)
		if(!vis[i] && cal(i)<4) dfs(i,cnt++);
	dijk();
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15072516.html