【题目描述】:
有一个n个点n条边的有向图(每个点一条出边,可能有自环,节点编号0~N-1),每条边为,意思是i指向f(i)的边权为w(i)的边,现在小A想知道,对于每个点的si和mi。
si:由i出发经过k条边,这k条边的权值和。
mi:由i出发经过k条边,这k条边的权值最小值。
【输入描述】:
第一行两个数n和k
第二行n个数f(i)
第三行n个数w(i)
【输出描述】:
每行两个数si和mi
【样例输入】:
7 3
1 2 3 4 3 2 6
6 3 1 4 2 2 3
【样例输出】:
10 1
8 1
7 1
10 2
8 2
7 1
9 3
【时间限制、数据范围及描述】:
时间:1s 空间:512M
30%的数据:n,k<=1000。
100%的数据:N<=10^5,k<=10^10,0<=f(i)<n,w(i)<=10^8。
题解:30分的暴力dfs如下(考场没想到是倍增阿巴阿巴阿巴~等我写好倍增就传代码子~
#include<cstdio> #include<iostream> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<bits/stdc++.h> typedef long long ll; using namespace std; const int N=100002; int n,k,ans1,ans2; int to[N],w[N]; void dfs(int mn,int now,int pace,int sum){ if(pace==k) { ans1=sum; ans2=mn; return ; } dfs(min(w[now],mn),to[now],pace+1,sum+w[now]); } int main(){ freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); scanf("%d %d",&n,&k); for(int i=0;i<n;i++) { scanf("%d",&to[i]); } for(int i=0;i<n;i++) { scanf("%d",& w[i]); } for(int i=0;i<n;i++) { dfs(0x3f3f3f3f,i,0,0); printf("%d %d ",ans1,ans2); } return 0; }
倍增就像这样阿巴阿巴~~~
#include<cstdio> #include<iostream> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<bits/stdc++.h> typedef long long ll; using namespace std; const int N=1e5+5; const int M=41; const int oo=0x3f3f3f3f; int n; ll k; int to[N][M],mn[N][M]; ll sum[N][M]; int main(){ scanf("%lld %lld",&n,&k); for(int i=0;i<n;i++) { scanf("%lld",&to[i][0]); } for(int i=0;i<n;i++){ scanf("%lld",&sum[i][0]); mn[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]; mn[j][i]=min(mn[j][i-1],mn[to[j][i-1]][i-1]); } } for(int i=0;i<n;i++){ ll s=0,kk=k; int m=oo,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,mn[p][j]); p=to[p][j]; } } printf("%lld %d ",s,m); } return 0; }