题目链接:
http://172.16.0.132/senior/#main/show/100026
题目:
有一个$n$个点$n$条边的有向图,每条边为$<i,f(i),w(i)>$,意思是$i$指向$f(i)$的边权为$w(i)$的边,现在小A想知道,对于每个点的$s_i$和$m_i$。
$s_i$:由$i$出发经过$k$条边,这$k$条边的权值和。
$m_i$:由$i$出发经过$k$条边,这$k$条边的权值最小值。
题解:
倍增即可(倍增的套路,转移是唯一的,体现在本题中是每个点出度为1)
$to[i][k]$表示节点i经过$2^k$个节点到达哪个节点,转移$to[i][k]=to[to[i][k-1]][k-1]$,边权和和路径最小值同理
#include<algorithm> #include<cstdio> #include<cstring> #include<iostream> using namespace std; typedef long long ll; const int N=1e5+15; const int M=40; const int inf=1e9; int n; ll k; int to[N][M],mi[N][M]; ll sum[N][M]; inline ll read() { char ch=getchar(); ll s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } int main() { n=read();k=read(); for (int i=0;i<n;i++) to[i][0]=read(); for (int i=0;i<n;i++) {sum[i][0]=read();mi[i][0]=sum[i][0];} for (int i=1;i<M;i++) { for (int j=0;j<n;j++) to[j][i]=to[to[j][i-1]][i-1]; for (int j=0;j<n;j++) { sum[j][i]=sum[j][i-1]+sum[to[j][i-1]][i-1]; mi[j][i]=min(mi[j][i-1],mi[to[j][i-1]][i-1]); } } for (int i=0;i<n;i++) { ll s=0,kk=k; int m=inf,p=i; for (int j=M-1;j>=0;j--) { if (!kk) break; if ((1ll<<j)<=kk) { kk-=1ll<<j; s+=sum[p][j]; m=min(m,mi[p][j]); p=to[p][j]; } } printf("%lld %d ",s,m); } return 0; }