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<=10^5,k<=10^10,0<=f(i)
Hint
Solution
观察到每个点的出边只有一条,那说明一个点运动的路径是固定不变的。
由于 K 的范围太大,直接模拟是不可能的,于是我们想到倍增算法。
设 F[i][j] 表示第 i 个点、运动了
2j 个点的状态。开三个域 to , sum , mn 分别表示其到的点、权值和 和 最小值 。
之后倍增求出 F 数组,再每个点 O(logK) 走一遍即可 。
这样的时间复杂度是 O(NlogK) ,可以通过本题。
Code
#include<cstdio>
#include<cmath>
using namespace std;
const int N=100001;
struct data
{
int to,mn;
long long sum;
}g[N][35];
long long k;
int f[N],w[N];
long long p[35];
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
int main()
{
int n=read();
scanf("%lld",&k);
int t=log2(k);
for(int i=p[0]=1;i<=t;i++) p[i]=p[i-1]*2;
for(int i=0;i<n;i++) g[i][0].to=f[i]=read();
for(int i=0;i<n;i++) g[i][0].sum=g[i][0].mn=w[i]=read();
for(int j=1;j<=t;j++)
for(int i=0;i<n;i++)
{
g[i][j].to=g[g[i][j-1].to][j-1].to;
g[i][j].sum=g[i][j-1].sum+g[g[i][j-1].to][j-1].sum;
g[i][j].mn=min(g[i][j-1].mn,g[g[i][j-1].to][j-1].mn);
}
for(int i=0;i<n;i++)
{
long long s=0,x=k;
int m=1e8,pos=i;
for(int j=t;j>=0 && x;j--)
if(x>=p[j])
{
x-=p[j];
m=min(m,g[pos][j].mn);
s+=g[pos][j].sum;
pos=g[pos][j].to;
}
printf("%lld %d\n",s,m);
}
return 0;
}