CF-702-E-倍增

702-E 题目大意

n个点,每个点有一条出边,边带权。给定整数k。求从每个节点出发经过k条边的路径上所有的边权和,以及最小的边权。


Solution

给定的图是基环树森林,从任意一个点出发无限走下去一定会进入环内。

倍增板子题,这里不详细解释什么是倍增数组,具体的可以网上自行学习。这里用到三个倍增数组:

  • to[i][j]:从第i个点出发走2j步到达的顶点。
  • sum[i][j]:从第i个点出发走2j步,路径上的边权和。
  • mn[i][j]:第i个点出发走2j步,路径上最小的边权。

倍增数组的构建,核心思路就是公式

2j=2j1+2j1

to数组为例,当前我们已将所有点走过2j1步的数组预处理完毕:

to[i][j]=to[to[i][j1]][j1]

从第i点出发先走2j1步到达to[i][j1]再走2j1即到达目标点。

查询节点ik条边后的边权和与最小边权:
由高到低枚举k的二进制位,如果k的第j位为1,则从当前点跳到to[i][j],同时更新所求的路径和以及最小的边权。

时间复杂度O(nlogk)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

void solve(){
    int n;
    ll k;
    cin>>n>>k;
    int P=35;
    vector<vector<int>> to(n+1,vector<int>(P));
    vector<vector<ll>> sum(n+1,vector<ll>(P));
    vector<vector<int>> mn(n+1,vector<int>(P,1e9));
    for(int i=0;i<n;i++){
        int j;
        cin>>j;
        to[i][0]=j;
    }
    for(int i=0;i<n;i++){
        int w;
        cin>>w;
        sum[i][0]=mn[i][0]=w;
    }
    for(int j=1;j<P;j++){
        for(int i=0;i<n;i++){
            to[i][j]=to[to[i][j-1]][j-1];
            sum[i][j]=sum[i][j-1]+sum[to[i][j-1]][j-1];
            mn[i][j]=min(mn[i][j-1],mn[to[i][j-1]][j-1]);
        }
    }
    for(int i=0;i<n;i++){
        ll path_sum=0;
        int path_mn=1e9,x=i;
        for(int i=P-1;~i;i--){
            if(k&(1LL<<i)){
                path_sum+=sum[x][i];
                path_mn=min(path_mn,mn[x][i]);
                x=to[x][i];
            }
        }
        cout<<path_sum<<" "<<path_mn<<'\n';
    }
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   fengxue-K  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示