[CSP-S 2022] 假期计划

[CSP-S 2022] 假期计划

题目传送门

题目大意

给定一个 $n \leq 2500,m \leq 10000$ 的无向图,有点权。求一条点权和最大的路径 $1\to A\to B\to C\to D\to 1$,满足:

  • $A,B,C,D$ 均不为 $1$,且互不相同;
  • 每一段路径上经过的点的数量小于等于 $k$。
  • 题目分析

    不难想到要通过 bfs 预处理出任意两点间距离。

    然后就可以写出一个 $O(nm + n^4)$ 暴力了:直接枚举 $A, B, C, D$ 并判断是否合法。

    此时注意到 $n \leq 2.5 \times 10^3$,也就是说:我们至多可以枚举两个点。

    于是我们折半一下,预处理对于每个 $B$,所有 $A$(即 $1 \to A \to B$)的最大、次大、次次大的值,求值时枚举 $B, C$ 并统计即可。

    时间复杂度为 $O(n(n + m))$。

    代码实现

    #include <bits/stdc++.h>
    
    #define rint register int
    #define endl '\n'
    #define int long long
    
    using namespace std;
    
    const int N = 2.5e3 + 5;
    const int M = 2e5 + 5;
    const int inf = 1e18;
    
    int n, m, k;
    int ans;
    int h[M], e[M], ne[M], idx;
    int dist[N][N], f[N][N], pos[N][3];
    int s[N];
    queue<int> q;
    
    void add(int a, int b)
    {
    	e[++idx] = b, ne[idx] = h[a], h[a] = idx;
    }
    
    void init(int n)
    {
    	for (rint i = 1; i <= n; i++)
    	{
    	    for (rint j = 1; j <= n; j++)
    	    {
    	        dist[i][j] = inf;
    	    }
    	}
    }
    
    void bfs(int s)
    {
    	dist[s][s] = 0;
    	q.push(s);
    	while(!q.empty())
    	{
    		int x = q.front();
    		q.pop();
    		for (rint i = h[x]; i; i = ne[i])
    		{
    			int y = e[i];
    			if (dist[s][y] == inf)
    			{
    				dist[s][y] = dist[s][x] + 1;
    				q.push(y);
    			}
    		}
    	}
    }
    
    signed main()
    {
    	cin >> n >> m >> k;
    	k += 1;
    	
    	init(n);
    	
    	for (rint i = 2; i <= n; i++)
    	{
    		cin >> s[i];
    	}
    	
    	for (rint i = 1; i <= m; i++)
    	{
    		int a, b;
    		cin >> a >> b;
    		add(a, b);
    		add(b, a);
    	}
    	
    	for (rint i = 1; i <= n; i++)
    	{
    		bfs(i);
    	}
    	
    	for (rint i = 1; i <= n; i++)
    	{
    		for (rint j = 1; j <= n; j++)
    		{
    			if (i != j && dist[1][i] <= k && dist[i][j] <= k)
    			{
    				f[i][j] = s[i] + s[j]; 
    			}
    			else
    			{
    				f[i][j] = -inf;
    			}
    		}
    	}
    	
    	for (rint i = 2; i <= n; i++)
    	{
    		pos[i][1] = i;
    		pos[i][2] = i;
    		pos[i][3] = i;
    		for (rint j = 2; j <= n; j++)
    		{
    			if (f[pos[i][1]][i] < f[j][i])
    			{
    				pos[i][3] = pos[i][2];
    				pos[i][2] = pos[i][1];
    				pos[i][1] = j;
    			}
    			else if (f[pos[i][2]][i] < f[j][i])
    			{
    				pos[i][3] = pos[i][2];
    				pos[i][2] = j;
    			}
    			else if (f[pos[i][3]][i] < f[j][i])
    			{
    				pos[i][3] = j;
    			}
    		}
    	}
    	
    	for (rint i = 2; i <= n; i++)
    	{
    		for (rint j = 2; j <= n; j++)
    		{
    			if (i != j && dist[i][j] <= k)
    			{
    				for (rint x = 1; x <= 3; x++)
    				{
    					for (rint y = 1; y <= 3; y++)
    					{
    						if (pos[i][x] != j && pos[i][x] != pos[j][y] && pos[j][y] != i)
    						{
    							ans = max(ans, f[pos[i][x]][i] + f[pos[j][y]][j]);
    							break;
    						}
    					}
    				}
    			}
    		}
    	}
    	
    	cout << ans << endl;
    	
    	return 0;
    }
    
posted @ 2023-10-14 18:26  南松的科技树  阅读(237)  评论(0)    收藏  举报