P8817 [CSP-S 2022] 假期计划

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

时隔一年后再来订正,终于理解了。降绿了才回来做。

题意简述:给一张无向图,点有点权,求\(\max{\{w_A+w_B+w_C+w_D\}}\),其中,\(A,B,C,D\)与一号结点构成一个环,环上相邻两点的距离\(\le k+1\)

数据范围\(5\le n\le 2500,1\le m\le 10000,0\le k\le 100\)

Solution

  • 一个直接的思路是暴力枚举一些点,在判断剩下的点是否合法。

显然\(n\)的范围允许我们枚举其中的两个点,一个直接的思路是枚举\(A,D\),再预处理出每个点在\(k\)次换乘能到达的点,这个集合的规模是\(O(n)\)的。

然后问题转化为两个点集\(S,T\),求两个点集中相邻的边的最大权值。这个问题无法在一个合适的时间复杂度内求解。

  • 于是不妨试试枚举\(B,C\)两点,预处理出每个点在\(k\)次换乘能到达的点,且在\(k\)次换乘内可以到达 1 号点。P.S.这其实是维护\(A,D\)所在的集合,\(A,D\)两点要可达 1 号点。

于是,我们只需每次找到点集\(S,T\)中,不相交的两个点中最大的权值。

但是,这个点集的规模是\(O(n)\)的,考虑优化。

观察性质,可以发现,如果考虑点\(A\),只用考虑是否与\(C,D\)重合。

于是,点集中的前三大的点中一定有一个合法决策P.S.最坏情况是最大点和次大点都重合。

于是我们有一个std::set维护每个点可达的点中,又与 1 号点可达的元素中权值前三大的点。

每次找出合法决策的时间复杂度为\(O(|S|^2log^2|S|)\)\(|S|\)代表集合中的元素个数,这里\(|S|=3\),总的时间复杂度为\(O(n^2|S|^2log^2|S|)\)

代码如下

#include<bits/stdc++.h>
#define int long long
typedef long long ll;
using namespace std;
const int N=3e3+10,M=2e5+10,INF=0x3f3f3f3f3f3f3f3f;

int head[N],ver[M],nxt[M],tot=1;
void add(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}

int n,m,k,d[N][N];
ll w[N],ans;
set<pair<int,int>>S[N];
bool v[N];

void bfs(int s,int d[]){
    queue<int>q;memset(v,0,sizeof(v));
    for(int i=1;i<=n;i++)d[i]=INF;
    d[s]=0;q.push(s);
    while(q.size()){
        int x=q.front();q.pop();
        if(v[x])continue;
        v[x]=1;
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i];
            if(d[y]>d[x]+1)d[y]=d[x]+1,q.push(y);
        }
    }
}

signed main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=2;i<=n;i++)scanf("%lld",&w[i]);
    for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    for(int i=1;i<=n;i++)bfs(i,d[i]);
    for(int i=2;i<=n;i++)for(int j=2;j<=n;j++){
        if(i==j)continue;
        if(d[i][j]<=k+1&&d[1][j]<=k+1)S[i].insert({w[j],j});
        if(S[i].size()>3)S[i].erase(S[i].begin());
    }
    for(int b=2;b<=n;b++)for(int c=2;c<=n;c++){
        if(d[b][c]>k+1||b==c)continue;
        for(auto a:S[b]){
            if(a.second==b||a.second==c)continue;
            for(auto d:S[c]){
                if(d.second==b||d.second==c||d.second==a.second)continue;
                ans=max(ans,w[b]+w[c]+w[a.second]+w[d.second]);
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2024-10-04 18:31  lichenyu_ac  阅读(38)  评论(0)    收藏  举报