题解 P8817 [CSP-S 2022] 假期计划

有趣的T1

Solution

首先看到 \(k\) 的限制,考虑怎么转化成 \(k=0\) 的情况:跑一遍全源最短路得到距离,再把距离 \(\le k+1\) 的点重新连边即可。由于边权均为 \(1\),可以直接跑 \(n\)\(\mathsf{dijkstra}\)\(\mathcal O(nm\log m)\) 的复杂度可以接受。

然后我们只要在重新连边的新图上找到最大的长度为 \(4\) 的回路就能解决。若直接搜索,复杂度为 \(\mathcal O(n^4)\),但是考虑到前两个景点和后两个景点是本质相同的,我们可以只枚举前两个点(后两个点也一样),结果记录在第二个点,即 \(f_B\) 上,然后枚举中间的 \(B\)(代表前两个点)和 \(C\)(代表后两个点)两点并合并答案。

但是这四个景点要求互不相同,而前两个点和后两个点的最优解有可能有重复的点,我们就可以不考虑最优解,而是存下前三优解(即最优解,次优解和次次优解),这样的好处是:枚举 \(B,C\) 时,若前两个点的最优解和次优解的 \(A\) 点或 \(B\) 点都和 \(C\)\(D\) 重复,就可以选择次次优解与之更新答案。枚举复杂度为 \(\mathcal O(n^2)\)

最后,一定要开 \(\mathsf{long\;long}\)!我有个朋友因为一个地方没开而造成ub,\(95\rightarrow 0\)

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace io{
	char buf[1<<21],*p1=buf,*p2=buf;
	inline char gc(){return p1==p2&&(p2=(p1=buf)+cin.rdbuf()->sgetn(buf,1<<21),p1==p2)?EOF:*p1++;}
	template<typename T>inline void read(T&x){
		x=0;char f=0,c=gc();
		while(!isdigit(c))f|=(c=='-'),c=gc();
		while(isdigit(c))x=x*10+c-'0',c=gc();
		if(f)x=-x;
	}
}using namespace io;
const int N=2505,M=1e4+5;
int n,m,k,dis[N][N];
ll ans,s[N];
forward_list<int>G[N],H[N];
bool vis[N];
priority_queue<pair<int,int>>q;
void dij(int S){
	while(!q.empty())q.pop();
	memset(vis,0,sizeof(vis));
	q.emplace(0,S);dis[S][S]=0;
	while(!q.empty()){
		int u=q.top().second;q.pop();
		vis[u]=1;
		for(int v:G[u]){
			if(dis[S][v]>dis[S][u]+1){
				dis[S][v]=dis[S][u]+1;
				if(!vis[v])q.emplace(-dis[S][v],v);
			}
		}
	}
	for(int i=1;i<=n;i++)
		if(i!=S&&dis[S][i]<=k+1)
			H[S].push_front(i);//重新连边,只连单向是因为跑i点的dij时也会向S连边
}
set<pair<ll,int>>f[N],g[N];
int main(){
	read(n),read(m),read(k);
	for(int i=2;i<=n;i++)read(s[i]);
	for(int i=1,u,v;i<=m;i++){
		read(u),read(v);
		G[u].push_front(v);
		G[v].push_front(u);
	}memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=n;i++)dij(i);
	for(int u:H[1])for(int v:H[u]){//枚举前两个点
		f[v].emplace(s[u]+s[v],u);
		if(f[v].size()>3)f[v].erase(f[v].begin());
	}
	for(int u=2;u<=n;u++)for(int v:H[u])
		for(auto x:f[u])for(auto y:f[v])
			if(x.second!=y.second&&x.second!=v&&y.second!=u)//判断景点不重复
				ans=max(ans,x.first+y.first);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-10-30 14:36  ADay526  阅读(359)  评论(0)    收藏  举报