Description
给出一张n个点,n条边的有向图。每个点的出度均为1,每条边有边权。
求:从每个点出发走k条边,所有方案中的边权和和最小值。
n<=10^5,k<=10^10
Solution
由于最近颓总在刷一些C、D题,今天闲着没事干随便找了道E题来刷~~
然而这道E题不是水题吗?
我才不会说我找了道通过人数三位数的E题呢
看到k这么大,肯定想到有一些神奇的log算法辣==
然后发现每个点出发,走k步走到的点是一定的,那么就倍增就好辣==
我一开始想到的类似快速幂的打法是什么鬼
哦,CF上long long类型要用I64d
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e5+5,inf=0x7fffffff;
ll mi[34],sum[N][34],ans[N],k;
int Min[N][34],to[N][34],MIN[N],n;
int main() {
scanf("%d%I64d",&n,&k);
mi[0]=1;fo(i,1,33) mi[i]=mi[i-1]*2;
fo(i,1,n) MIN[i]=inf;
fo(i,1,n) scanf("%d",&to[i][0]),to[i][0]++;
fo(i,1,n) scanf("%I64d",&sum[i][0]),Min[i][0]=sum[i][0];
fo(j,1,33) fo(i,1,n) {
to[i][j]=to[to[i][j-1]][j-1];
sum[i][j]=sum[i][j-1]+sum[to[i][j-1]][j-1];
Min[i][j]=min(Min[i][j-1],Min[to[i][j-1]][j-1]);
}
fo(i,1,n) {
ll now=0;int x=i;
fd(j,33,0) if (now+mi[j]<=k) {
ans[i]+=sum[x][j];
MIN[i]=min(MIN[i],Min[x][j]);
now+=mi[j];x=to[x][j];
}
}
fo(i,1,n) printf("%I64d %d\n",ans[i],MIN[i]);
}