1 条题解

  • 0
    @ 2026-4-30 1:44:14

    显然有一个想法是,确定了第一个人移动的轮数 TT 后,每个人的答案是独立的,把以 ii 为起点的人在移动 TT 轮后的最小代价是 fi(T)f_i(T),我们对 fi(T)f_i(T) 进行刻画。

    首先对最优策略有一个观察,我们必然只有两种 case:

    1. 初始走到一个停止格 uu,之后每次移动离开 uu 再回到 uu
    2. 先花若干步走到一个停止格 uu,且其邻域内有一个停止格 vv,之后每次移动在 u,vu,v 间反复横跳。

    第一种 case 较为容易,假设距离点 uu 最近的停止点的距离为 dd,那么有 fu(T)d+2T2f_u(T)\gets d+2T-2

    第二种 case,假设我们花了 kk 步到达一个合法的终止位置,总的距离为 dd,那么我们浪费了 kk 的时间,应该有 fu(T)dk+Tf_u(T)\gets d-k+T,给每个终止节点 1-1 的权值,每条边 11 的权值,跑一遍最短路即可。

    最后我们的 fu(T)f_u(T) 形如 min(c2+2T,c1+T)\min(c_2+2T,c_1+T)

    fXp(T)=F(T)\sum f_{X_p}(T)=F(T),那我们要解决的问题形如:

    对于每个 pp,找到一条 X1pX_1\to p 的路径,假设经过了 tt 个终止点,路径长度为 dd,我们要最小化 F(k)+dF(k)+d

    这个完全没法用数据结构维护,我们尝试使用一些根号做法。

    假设 KK 比较大,那么 tt 增加 11 后,f(t)f(t) 至少增加 K1K-1,而 tt 增大对 dd 的贡献至多只有 n-n,也就是假设 tt 的最小值是 tmint_{\min},那么我们只需要考虑 [tmin,tmin+nK1][t_{\min},t_{\min}+\frac{n}{K-1}] 范围内的 tt 即可,分层图最短路即可 O(n2K)O(\frac{n^2}{K})

    KK 比较小的时候,发现 FF 是一个 O(K)O(K) 段的凸的分段函数,并且每一段都是一条线段。

    把线段变成直线,我们认为可以任选其中一条直线,然后一个惊人的发现是,这样不会把答案算小!因为我们是在一个凸包上面嘛,如果选错了直线答案必然更大。

    那把这 O(K)O(K) 条直线拉出来,把贡献拆到单点上面,跑最短路即可。时间复杂度 O(nKlogn)O(nK\log n)

    平衡一下,时间复杂度 O(nnlogn)O(n\sqrt{n\log n})

    corner 比较多,写起来也比较烦。 ::::info[code]

    const int N=5e4+5,K=505;
    const ll inf=1e16;
    int n,m,k;
    vector<int> to[N];
    void add(int u,int v){to[u].pb(v),to[v].pb(u);}
    ll F[N];
    int s[N],ty[N],tg[N];
    int c1[N],c2[N];
    int dis[N];
    void solve2(){  // calculate c2
        queue<int> q;
        memset(dis,0x3f,sizeof dis);
        for(int i=1;i<=n;i++)if(ty[i])q.push(i),dis[i]=0;
        while(!q.empty()){
            int u=q.front(); q.pop();
            for(int v:to[u])if(dis[v]>dis[u]+1){
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
        for(int i=1;i<=n;i++)c2[i]=dis[i]-(ty[i]?0:2);
    }
    bool vis[N];
    void solve1(){  // calculate c1
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        memset(dis,0x3f,sizeof dis);
        for(int i=1;i<=n;i++)if(tg[i])pq.push(mkp(0,i)),dis[i]=0;
        while(!pq.empty()){
            int u=pq.top().se; pq.pop();
            if(vis[u])continue; vis[u]=1;
            for(int v:to[u]){
                int w=ty[u]?0:1;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    pq.push(mkp(dis[v],v));
                }
            }
        }
        for(int i=1;i<=n;i++)c1[i]=dis[i];
    }
    ll dif[N],dif2[N];
    vector<int> fg;
    void solve6(){
        for(int j=2;j<=k;j++){
            int i=s[j];
            int _t=c1[i]-c2[i];
            chkmin(_t,n+1),chkmax(_t,1);
            fg.pb(_t);
            dif[1]+=c2[i],dif[_t]+=c1[i]-c2[i]-_t+1;
            dif2[1]+=2,dif2[_t]--;
        }
        for(int i=1;i<=n;i++)dif2[i]+=dif2[i-1],dif[i]+=dif2[i];
        for(int i=1;i<=n;i++)dif[i]+=dif[i-1],F[i]=dif[i];
        fg.pb(1),fg.pb(n); fg.pb(n+1);
        sort(fg.begin(),fg.end()),fg.erase(unique(fg.begin(),fg.end()),fg.end());
    }
    int d[N][K],_d[N];
    void solve2p5(){
        memset(_d,0x3f,sizeof _d);
        deque<int> q;
        q.push_front(s[1]);
        _d[s[1]]=0;
        while(!q.empty()){
            int u=q.front(); q.pop_front();
            for(int v:to[u]){
                int dv=_d[u]+ty[v];
                if(dv<_d[v]){
                    _d[v]=dv;
                    if(!ty[v])q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
    void solve3(){
        memset(d,0x3f,sizeof d);
        queue<pii> q;
        q.push(mkp(s[1],0));
        d[s[1]][0]=0;
        while(!q.empty()){
            int u=q.front().fi,c=q.front().se;
            q.pop();
            for(int v:to[u]){
                int nd=d[u][c]+1,nc=c+ty[v]+_d[u]-_d[v];
                if(nc>=K)continue;
                if(nd<d[v][nc])
                    d[v][nc]=nd,q.push(mkp(v,nc));
            }
        }
    }
    ll ans[N],dd[N*3];
    bool vv[N*3];
    void solve5(ll b,ll k){
        priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > pq;
        pq.push(mkp(0,s[1]*3));
        memset(dd,0x3f,sizeof dd),memset(vv,0,sizeof vv);
        dd[s[1]*3]=0;
        while(!pq.empty()){
            int u=pq.top().se; pq.pop();
            if(vv[u])continue; vv[u]=1;
            for(int v:to[u/3]){
                ll nd=dd[u]+1+ty[v]*k;
                int nv=v*3;
                int c=u%3; c+=ty[v]; chkmin(c,2);
                nv+=c;
                if(nd<dd[nv])
                    dd[nv]=nd,pq.push(mkp(dd[nv],nv));
            }
        }
        for(int i=1;i<=n;i++)if(i!=s[1]){
            chkmin(ans[i],b+dd[i*3+2]-ty[i]*k);
            if(!ty[i])chkmin(ans[i],b+dd[i*3+1]-ty[i]*k);
        }
    }
    void solve4(){
        if(k<=150){
            int ls=0;
            for(int i:fg){
                if(ls && i<=n){
                    ll k=F[i]-F[i-1],b=F[i]-i*k;
                    solve5(b,k);
                }
                ls=i;
            }
        }
    }
    void solve7(){
        for(int i=1;i<=n;i++){
            for(int j=0;j<K && j+_d[i]-ty[i]<=n;j++)
                chkmin(ans[i],F[j+_d[i]-ty[i]]+d[i][j]);
        }
    }
    int main(){
        n=read(),m=read(),k=read();
        while(m--)add(read(),read());
        char c=gc();
        while(!isdigit(c))c=gc();
        for(int i=1;i<=n;i++)ty[i]=c-'0',c=gc();
        for(int i=1;i<=k;i++)s[i]=read();
        for(int i=1;i<=n;i++)for(int v:to[i])
            if(ty[i]&&ty[v])tg[i]=1;
        memset(ans,0x3f,sizeof ans);
        solve2(),solve1(),solve6();
        solve2p5(),solve3(),solve4();
        solve7();
        for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
        return 0;
    }
    //  Think twice,code once
    

    ::::

    • 1

    信息

    ID
    7549
    时间
    4000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者