【JZOJ100026】图

版权声明:欢迎dalao指出错误暴踩本SD,蒟蒻写的博客转载也要吱一声 https://blog.csdn.net/enjoy_pascal/article/details/82952052

description

有一个n个点n条边的有向图,每条边为<i,f(i),w(i)>,意思是i指向f(i)的边权为w(i)的边,现在小A想知道,对于每个点的si和mi。
si:由i出发经过k条边,这k条边的权值和。
mi:由i出发经过k条边,这k条边的权值最小值。


analysis

  • 正解倍增

  • 每个点出度是 1 1 ,就是说点会向指定的方向跳,所以就算不是树也可以倍增

  • 可以 O ( n log 2 k ) O(n\log_2k) 时间预处理出第 i i 点跳 2 j 2^j 步所到的边权和边权最小值,和普通倍增一样

  • 然后每个点再走 log 2 k \log_2k 步,走的过程中统计答案

  • 不用判环,因为倍增预处理的信息一直都在一个环里面转


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 100005
#define ll long long
#define INF 1000000007
#define fo(i,a,b) for (register ll i=a;i<=b;++i)
#define fd(i,a,b) for (register ll i=a;i>=b;--i)

using namespace std;

ll a[MAXN],b[MAXN];
ll pow[40];
ll n,k;

struct node
{
	ll tov,sum,mn;
}f[MAXN][40];

__attribute__((optimize("-O3")))
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while (ch<'0' || '9'<ch)
    {
        if (ch=='-')f=-1;
        ch=getchar();   
    }
    while ('0'<=ch && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
__attribute__((optimize("-O3")))
int main()
{
	//freopen("T2.in","r",stdin);
	n=read()-1,k=read();
	fo(i,0,n)f[i][0].tov=read();
	fo(i,0,n)f[i][0].sum=f[i][0].mn=read();
	pow[0]=1;
	fo(i,1,35)pow[i]=pow[i-1]*2;
	fo(j,1,35)
	fo(i,0,n)
	{
		f[i][j].tov=f[f[i][j-1].tov][j-1].tov;
		f[i][j].sum=f[i][j-1].sum+f[f[i][j-1].tov][j-1].sum;
		f[i][j].mn=min(f[i][j-1].mn,f[f[i][j-1].tov][j-1].mn);
	}
	fo(i,0,n)
	{
		ll s=0,m=INF,where=i;
		for (register ll j=35,l=k;j>=0 && l;--j)
		if (l>=pow[j])
		{
			l-=pow[j];
			m=min(m,f[where][j].mn),
			s+=f[where][j].sum;
			where=f[where][j].tov;
		}
		printf("%lld %lld\n",s,m);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/enjoy_pascal/article/details/82952052