题解 P13271 [NOI2025] 机器人

更好的阅读体验

铁牌选手凑个热闹,这也是 NOI.jpg

Solution

观察到对于每个点,有效的 \(p\) 只有 \(d_i\) 个,这样状态总数就是 \(\sum d_i=m\)

考虑拆点,每个点拆成 \(d_i\) 个点表示它的所有 \(p\) 的状态。单个点的状态之间通过 \(v\)\(w\) 建边。在处理原图的边的时候如果 \(v\) 的状态不足这条边的 \(p\),连到 \(v\) 的最大编号,简单计算边权即可。最后跑一个最短路就做完了。

由于还要建立一个点计算实际的最短路,总点数是 \(N=n+m\),总边数是 \(M=2m+m=3m\),时间复杂度就是 \(O(M\log M)\)

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1e18;
const int N=3e5+10;
int n,m,k;
int tot;
int d[N];
struct node{
    int v;
    ll w;
    friend bool operator<(node x,node y){
        return x.w>y.w;
    }
};
vector<node>g[N*2];
vector<int>num[N];
int U[N],V[N],ID[N],cnt;
ll W[N];
int w1[N],w2[N];
ll sum[N];
void add(int u,int v,ll w){
    g[u].push_back({v,w});
}
ll dis[N];
void dij(int s){
    for(int i=1;i<=tot;i++){
        dis[i]=inf;
    }
    priority_queue<node>q;
    q.push({s,0});
    dis[s]=0;
    while(!q.empty()){
        node tmp=q.top();
        q.pop();
        int x=tmp.v;
        if(tmp.w>dis[x]){
            continue;
        }
        for(auto i:g[x]){
            int v=i.v;
            ll w=i.w;
            if(dis[v]>dis[x]+w){
                dis[v]=dis[x]+w;
                q.push({v,dis[v]});
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int c;
    cin>>c>>n>>m>>k;
    for(int i=1;i<k;i++){
        cin>>w1[i];
    }
    for(int i=2;i<=k;i++){
        cin>>w2[i];
        sum[i]=sum[i-1]+w2[i];
    }
    for(int i=1;i<=n;i++){
        cin>>d[i];
        num[i].resize(d[i]+1);
        num[i][0]=++tot;
        for(int j=1;j<=d[i];j++){
            num[i][j]=++tot;
            ++cnt;
            U[cnt]=i;
            ID[cnt]=j;
            cin>>V[cnt]>>W[cnt];
        }
        for(int j=1;j<=d[i];j++){
            if(j<d[i]){
                add(num[i][j],num[i][j+1],w1[j]);
            }
            if(j>1){
                add(num[i][j],num[i][j-1],w2[j]);
            }
        }
    }
    for(int i=1;i<=m;i++){
        int u=U[i],v=V[i],id=ID[i];
        ll w=W[i];
        add(num[u][id],num[v][0],w);
        if(id>d[v]){
            add(num[u][id],num[v][d[v]],w+sum[id]-sum[d[v]]);
        }else{
            add(num[u][id],num[v][id],w);
        }
    }
    if(!d[1]){
        cout<<"0 ";
        for(int i=1;i<=n;i++){
            cout<<"-1 ";
        }
        cout<<'\n';
        return 0;
    }
    dij(num[1][1]);
    cout<<"0 ";
    for(int i=2;i<=n;i++){
        ll ans=dis[num[i][0]];
        if(ans>1e17){
            cout<<"-1 ";
        }else{
            cout<<ans<<' ';
        }
    }
    cout<<'\n';
    return 0;
}

一些闲话

赛时不到 1h 即想出了正解,但是因为谜之原因(可能是太困了)导致我认为状态总数是 \(O(k^2)\),进而导致我转向别的思路并成功没场切绿题打铁呜呜。

posted @ 2025-07-18 08:37  Z3k7223  阅读(59)  评论(0)    收藏  举报