P8817 [CSP-S 2022] 假期计划 题解
思路
看一眼数据范围。
发现是一道 \(O(n^2)\) 可做题。
由于题目中路径的长度只有 \(4\) 个点,可以从这一方面继续入手。
考虑预处理。
我们设 \(dp_i,j\) 为从 \(1\) 到 \(x\) 再从 \(x\) 到 \(i\) 这样一条合法路径第 \(j\) 大的权值。
容易发现我们最多只需要处理出前四大的即可,因为需要不同的四个点。
这样,我们就相当于有了 \(\text{C}\) 点和 \(\text{D}\) 点后,我们可以直接得到 \(A\) 点和 \(B\) 点。
复杂度:\(O(n^2)\)。
有一个小细节,由于这道题的合法路径判断需要知道两点之间的距离,即全源最短路。
发现边权全部是一可以直接 \(\text{bfs}\) 处理出来。
但是我考场上不知道为什么没有想到写了一个全源最短路。
所以考场代码就变为了:\(O(n^2\log n)\) 的劣复杂度。
但 \(2500\) 应该问题不大。
Code
放一个考场代码。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2510;
int n , m , cnt , ans , f[N][4][2];
int k , s[N] , vis[N] , head[N] , dis[N][N];
struct edge
{
int to , nxt , val;
}e[100000];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void add(int x , int y)
{
e[++cnt] = {y , head[x] , 1} , head[x] = cnt;
e[++cnt] = {x , head[y] , 1} , head[y] = cnt;
}
inline void dij(int s)
{
priority_queue<pair<int , int> , vector<pair<int , int> > , greater<pair<int , int> > > q;
q.push({0 , s}) , memset(dis[s] , 0x3f , sizeof dis[s]);
dis[s][s] = 0 , memset(vis , 0 , sizeof vis);
while(q.empty() == 0)
{
int x = q.top().second; q.pop();
vis[x] = 1;
for(int i = head[x];i;i = e[i].nxt)
{
if(dis[s][e[i].to] > dis[s][x] + e[i].val)
{
dis[s][e[i].to] = dis[s][x] + e[i].val;
if(!vis[e[i].to]) q.push({dis[s][e[i].to] , e[i].to});
}
}
}
}
inline bool check(int x , int y , int z)
{
int dis1 = dis[x][y] - 1;
int dis2 = dis[y][z] - 1;
return dis1 <= k && dis2 <= k;
}
inline bool check(int x , int y , int z , int u)
{
return (x != y && y != z && z != u && x != z && x != u && y != u);
}
inline void add(int x , int z , int id)
{
int flag = 4;
for(int i = 0;i < 4;i++)
if(f[x][i][1] < z)
{ flag = i; break; }
if(flag != 4)
{
for(int i = 3;i > flag;i--)
swap(f[x][i][1] , f[x][i - 1][1]),
swap(f[x][i][0] , f[x][i - 1][0]);
f[x][flag][1] = z , f[x][flag][0] = id;
}
}
signed main()
{
n = read() , m = read() , k = read();
for(int i = 2;i <= n;i++)
s[i] = read();
for(int i = 1;i <= m;i++)
{
int x = read() , y = read();
add(x , y);
}
for(int i = 1;i <= n;i++)
dij(i);
for(int i = 2;i <= n;i++)
for(int j = 2;j <= n;j++)
{
if(i == j) continue;
if(check(1 , i , j))
add(j , s[i] , i);
}
memset(vis , 0 , sizeof vis);
for(int i = 2;i <= n;i++)
for(int j = 2;j <= n;j++)
if(i != j && dis[i][j] - 1 <= k)
{
int sum = 0;
for(int k1 = 0;k1 <= 3;k1++)
{
if(!f[i][k1][0]) continue;
for(int k2 = 0;k2 <= 3;k2++)
{
if(!f[j][k2][0]) continue;
if(check(i , j , f[i][k1][0] , f[j][k2][0]))
sum = max(sum , s[i] + s[j] + f[i][k1][1] + f[j][k2][1]);
}
}
ans = max(ans , sum);
}
cout << ans << endl;
return 0;
}
\(\text{bfs}\) 也放一个吧。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2510;
int n , m , cnt , ans , f[N][4][2];
int k , s[N] , vis[N] , head[N] , dis[N][N];
struct edge
{
int to , nxt , val;
}e[100000];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void add(int x , int y)
{
e[++cnt] = {y , head[x] , 1} , head[x] = cnt;
e[++cnt] = {x , head[y] , 1} , head[y] = cnt;
}
inline void bfs(int s)
{
queue<int> q;
memset(dis[s] , 0x3f , sizeof dis[s]);
q.push(s) , dis[s][s] = 0;
while(q.empty() == 0)
{
int x = q.front(); q.pop();
for(int i = head[x];i;i = e[i].nxt)
if(dis[s][e[i].to] == 0x3f3f3f3f3f3f3f3f)
dis[s][e[i].to] = dis[s][x] + e[i].val,
q.push(e[i].to);
}
}
inline bool check(int x , int y , int z)
{
int dis1 = dis[x][y] - 1;
int dis2 = dis[y][z] - 1;
return dis1 <= k && dis2 <= k;
}
inline bool check(int x , int y , int z , int u)
{
return (x != y && y != z && z != u && x != z && x != u && y != u);
}
inline void add(int x , int z , int id)
{
int flag = 4;
for(int i = 0;i < 4;i++)
if(f[x][i][1] < z)
{ flag = i; break; }
if(flag != 4)
{
for(int i = 3;i > flag;i--)
swap(f[x][i][1] , f[x][i - 1][1]),
swap(f[x][i][0] , f[x][i - 1][0]);
f[x][flag][1] = z , f[x][flag][0] = id;
}
}
signed main()
{
n = read() , m = read() , k = read();
for(int i = 2;i <= n;i++)
s[i] = read();
for(int i = 1;i <= m;i++)
{
int x = read() , y = read();
add(x , y);
}
for(int i = 1;i <= n;i++)
bfs(i);
for(int i = 2;i <= n;i++)
for(int j = 2;j <= n;j++)
{
if(i == j) continue;
if(check(1 , i , j))
add(j , s[i] , i);
}
memset(vis , 0 , sizeof vis);
for(int i = 2;i <= n;i++)
for(int j = 2;j <= n;j++)
if(i != j && dis[i][j] - 1 <= k)
{
int sum = 0;
for(int k1 = 0;k1 <= 3;k1++)
{
if(!f[i][k1][0]) continue;
for(int k2 = 0;k2 <= 3;k2++)
{
if(!f[j][k2][0]) continue;
if(check(i , j , f[i][k1][0] , f[j][k2][0]))
sum = max(sum , s[i] + s[j] + f[i][k1][1] + f[j][k2][1]);
}
}
ans = max(ans , sum);
}
cout << ans << endl;
return 0;
}