1. 程式人生 > 其它 >「JOI 2021 Final」機器人

「JOI 2021 Final」機器人

「JOI 2021 Final」機器人


​ 首先走一條路且有其他路的顏色與這條路相同,有兩種情況。

  1. 把這條路道路的顏色改變
  2. 把其他與這條路顏色相同的路的顏色改變。

​ 又因為我們有 \(M\) 種顏色,一共只有 \(M\) 條路,所以我們可以做到將一條邊塗改成一種獨一無二的顏色。

​ 由於重複經過一個點不可能成為最優方案,所以我們可以直接這麼建邊跑一段最短路。

​ 但是這麼跑出來的答案是偏大的。因為假設我們通過 \(1\) 方式轉移,那麼下一次走相同顏色的邊就可以少改變一條邊的顏色。

​ 而如果通過 \(2\) 方式轉移,則沒有影響,因為如果通過 \(2\) 方式改變邊的顏色會對之後產生影響的話,不如直接走會產生影響的那條邊,這樣更優。

​ 所以我們還得建邊,對於點 \(u,v\),我們要新建邊來滿足這樣的需求:從 \(u\) 走顏色為 \(c\) 的邊到 \(v\),然後在 \(v\) 接著走顏色為 \(c\) 的邊,並且從 \(u\)\(v\) 是通過 \(1\) 方式,從 \(v\) 走出是通過 \(2\) 方式。

​ 建好新邊後再跑一遍最短路就行了,我們可以採用虛點的策略去建新邊,那麼點數不超過 \(n+2\times m\),邊數不超過 \(6\times m\)

程式碼如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 6e5+5;
const ll INF = 1e17;
int n,m;
ll dis[MAXN],cnt[MAXN];
struct E
{
	int to;ll w;int c;
};
vector <E> g[MAXN],e[MAXN];
struct node
{
	int p;ll dis;
	bool operator < (const node&x)const
	{
		return dis>x.dis;
	}
};
bool vis[MAXN];
void dij()
{
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	priority_queue <node> q;
	q.push(node{1,0});
	dis[1]=0;
	while(!q.empty())
	{
		node now=q.top();
		q.pop();
		int p=now.p;
		if(vis[p]) continue;
		vis[p]=1;
		for(int i=0;i<e[p].size();++i)
		{
			int to=e[p][i].to;ll w=e[p][i].w;
			if(dis[to]>dis[p]+w)
			{
				dis[to]=dis[p]+w;
				q.push(node{to,dis[to]});
			}
		}			
	}
	if(dis[n]>INF) printf("-1\n");
	else printf("%lld\n",dis[n]);
}
map <int,int> mp[MAXN];
void dfs(int p)
{
	vis[p]=1;
	for(int i=0;i<g[p].size();++i)
		cnt[g[p][i].c]+=g[p][i].w;
	for(int i=0;i<g[p].size();++i)
	{
		int to=g[p][i].to;
		e[p].push_back(E{to,cnt[g[p][i].c]>g[p][i].w?min(g[p][i].w,cnt[g[p][i].c]-g[p][i].w):0,0});
		e[p].push_back(E{mp[to][g[p][i].c],0,0});
		e[mp[p][g[p][i].c]].push_back(E{to,cnt[g[p][i].c]-g[p][i].w,0});
	}
	for(int i=0;i<g[p].size();++i)
		cnt[g[p][i].c]=0;
	for(int i=0;i<g[p].size();++i)
	{
		int to=g[p][i].to;
		if(vis[to]) continue;
		dfs(to);
	}
}
int main()
{
//	freopen("robot.in","r",stdin);
//	freopen("robot.out","w",stdout);
	scanf("%d %d",&n,&m);
	int Node=n;
	for(int i=1;i<=m;++i)
	{
		int u,v,c;ll p;
		scanf("%d %d %d %lld",&u,&v,&c,&p);
		g[u].push_back(E{v,p,c});
		g[v].push_back(E{u,p,c});
		if(!mp[u].count(c)) mp[u][c]=++Node;
		if(!mp[v].count(c)) mp[v][c]=++Node;
	}
	dfs(1);
	dij();
	return 0;
}

路漫漫其修遠兮,吾將上下而求索。