JZOJ 100026. 【NOIP2017提高A组模拟7.7】图

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

JZOJ 100026. 【NOIP2017提高A组模拟7.7】图_第1张图片

Solution

  • 观察到每个点的出边只有一条,那说明一个点运动的路径是固定不变的。

  • 由于 K 的范围太大,直接模拟是不可能的,于是我们想到倍增算法。

  • F[i][j] 表示第 i 个点、运动了 2j 个点的状态。

  • 开三个域 to sum mn 分别表示其到的点、权值和 和 最小值 。

  • 之后倍增求出 F 数组,再每个点 O(logK) 走一遍即可 。

  • 这样的时间复杂度是 O(NlogK) ,可以通过本题。

Code

#include
#include
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 xint 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;i0].to=f[i]=read();
    for(int i=0;i0].sum=g[i][0].mn=w[i]=read();
    for(int j=1;j<=t;j++)
        for(int i=0;i1].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;ilong 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;
}

你可能感兴趣的:(倍增,图论)