题面
Description
有一个n个点n条边的有向图,每条边为<i,f(i),w(i)>,意思是i指向f(i)的边权为w(i)的边,现在小A想知道,对于每个点的si和mi。
si:由i出发经过k条边,这k条边的权值和。
mi:由i出发经过k条边,这k条边的权值最小值。
Input
第一行两个数n和k
第二行n个数f(i)
第三行n个数w(i)
Output
每行两个数si和mi
Sample Input
7 3
1 2 3 4 3 2 6
6 3 1 4 2 2 3
Sample Output
10 1
8 1
7 1
10 2
8 2
7 1
9 3
Data Constraint
30%的数据:n,k<=1000。
100%的数据:N<=105,k<=1010,0<=f(i)<n,w(i)<=10^8。
Hint
思路
首先这一题中每一个点的出度有且只有1。
那我们这时候就很容易想到用倍增去实现它。
设
f
i
,
k
f_{i,k}
fi,k表示在
i
i
i号点跳
2
k
2^k
2k步的和。
g
i
,
k
g_{i,k}
gi,k表示在
i
i
i号点跳
2
k
2^k
2k步的最小值。
g
o
i
,
k
go_{i,k}
goi,k表示在
i
i
i号点跳
2
k
2^k
2k步到达的点。
显然,我们有:
f
i
,
k
=
f
i
,
k
−
1
+
f
g
o
i
,
k
−
1
,
k
−
1
g
i
,
k
=
m
i
n
(
g
i
,
k
−
1
,
g
g
o
i
,
k
−
1
,
k
−
1
)
g
o
i
,
k
=
g
o
g
o
i
,
k
−
1
,
k
−
1
f_{i,k}=f_{i,k-1}+f_{go_{i,k-1},k-1}\\ g_{i,k}=min(g_{i,k-1},g_{go_{i,k-1},k-1})\\ go_{i,k}=go_{go_{i,k-1},k-1}
fi,k=fi,k−1+fgoi,k−1,k−1gi,k=min(gi,k−1,ggoi,k−1,k−1)goi,k=gogoi,k−1,k−1
初始化:
f
i
,
0
=
g
i
,
0
=
w
i
g
o
i
,
0
=
t
o
i
f_{i,0}=g_{i,0}=w_i\\ go_{i,0}=to_i
fi,0=gi,0=wigoi,0=toi
Code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#define N 200005
using namespace std;
int n,to[N],go[N][40];
long long f[N][40],g[N][40],k;
void get(int now,long long rest) {
long long sum=0;
long long minx=1e8;
long long s=34;
while(rest) {
while((1ll<<s)>rest) s--;
sum+=f[now][s];
minx=min(minx,g[now][s]);
now=go[now][s];
rest-=(1ll<<s);
}
printf("%lld %lld\n",sum,minx);
}
int main() {
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&to[i]),to[i]++,go[i][0]=to[i];
for(int i=1;i<=n;i++) {
long long w;
scanf("%lld",&w);
f[i][0]=g[i][0]=w;
}
for(int j=1;j<=34;j++)
for(int i=1;i<=n;i++) {
if((1<<j)>k) break;
f[i][j]=f[i][j-1]+f[go[i][j-1]][j-1];
g[i][j]=min(g[i][j-1],g[go[i][j-1]][j-1]);
go[i][j]=go[go[i][j-1]][j-1];
}
for(int i=1;i<=n;i++)
get(i,k);
}