[51Nod算法马拉松14 F] 斐波那契树

这里写图片描述

官方题解用树上倍增做的…转移感觉超级厉害也超级麻烦我没有看懂 QAQ 所以来分享一下点分 + 矩阵的做法… 分治跟 SCOI 2016 D1T2 其实挺像的就不多说了 有什么细节问题可以看代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bitset>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <iterator>
#include <vector>
#include <queue>
#include <set>
#include <map>

#define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i)
#define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i)
#define x first
#define y second

using namespace std;
typedef long long LL;

template<typename T> inline void upMax(T & x, T y) { x < y ? x = y : 0; }
template<typename T> inline void upMin(T & x, T y) { x > y ? x = y : 0; }

template<typename T>
inline void read(T & x)
{
    char c;
    while ((c = getchar()) < '0' || c > '9') ;
    for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0') ;
}

const int inf = 0x3f3f3f3f;
const int mo = 1e9 + 9;
const int N = 1e5 + 10;

inline int inc(int x, int y) { return (x += y) >= mo ? x - mo : x; }
inline int dec(int x, int y) { return (x -= y) < 0 ? x + mo : x; }

struct matrix
{
    LL e[2][2];

    inline void set(int f)
    {
        memset(e, 0, sizeof(e));
        e[0][0] = e[1][1] = f;
    }
    inline matrix operator + (const matrix & b) const
    {
        static matrix ret;
        rep (i, 0, 1) rep (j, 0, 1)
            (ret.e[i][j] = e[i][j] + b.e[i][j]) >= mo ? ret.e[i][j] -= mo : 0;
        return ret;
    }
    inline matrix operator - (const matrix & b) const 
    {
        static matrix ret;
        rep (i, 0, 1) rep (j, 0, 1) 
            (ret.e[i][j] = e[i][j] - b.e[i][j]) < 0 ? ret.e[i][j] += mo : 0;
        return ret;
    }
    inline matrix operator * (const matrix & b) const
    {
        static matrix ret;
        rep (i, 0, 1) rep (j, 0, 1)
            ret.e[i][j] = (e[i][0] * b.e[0][j] + e[i][1] * b.e[1][j]) % mo;
        return ret;
    }
    inline matrix operator * (const int b) const
    {
        static matrix ret;
        rep (i, 0, 1) rep (j, 0, 1) 
            ret.e[i][j] = e[i][j] * b % mo;
        return ret;
    }
};

matrix fib[N];

struct edge
{
    int to;
    edge *n;
};

struct Data
{
    int x, y, id, v;

    inline bool operator < (const Data & b) const
    {
        return v < b.v;
    }
} qus[N];

int Ans[N];

namespace myTree
{
    edge poolEdge[N * 2], *cur = poolEdge, *head[N];

    inline void addEdge(int x, int y)
    {
        cur->to = y, cur->n = head[x], head[x] = cur++;
        cur->to = x, cur->n = head[y], head[y] = cur++;
    }

    int pSize[N], TOT;

    void initDfs(int x, int fa)
    {
        pSize[x] = 1;
        for (edge *p = head[x]; p; p = p->n) if (p->to != fa)
        {
            initDfs(p->to, x);
            pSize[x] += pSize[p->to];
        }
    }

    inline int getSize(int x, int y)
    {
        return pSize[x] > pSize[y] ? pSize[y] : TOT - pSize[x];
    }

    bool del[N];

    int nodeTot, center, cVal;
    int size[N];

    void calcRoot(int x, int fa)
    {
        int mx = 0;
        size[x] = 1;
        for (edge *p = head[x]; p; p = p->n) 
        {
            int to = p->to;
            if (del[to] || to == fa)
                continue ;

            calcRoot(to, x);
            size[x] += size[to];
            upMax(mx, size[to]);
        }
        upMax(mx, nodeTot - size[x]);
        if (mx < cVal)
            center = x, cVal = mx;
    }

    matrix f[N], h[N];
    LL g[N], num[N];
    int deep[N], top[N], col[N], tim;

    void calc_1(int x, int fa)
    {
        col[x] = tim;
        g[x] = 0;
        num[x] = 1;
        for (edge *p = head[x]; p; p = p->n)
        {
            int to = p->to;

            if (to == fa) 
                continue ;

            if (del[to])
            {
                int tmp = getSize(x, to);
                g[x] = inc(g[x], num[x] * tmp % mo);
                num[x] += tmp;
                continue ;
            }
            else 
            {
                top[to] = top[x];
                calc_1(to, x);
                g[x] = inc(g[x], num[x] * num[to] % mo);
                num[x] += num[to];
            }
        }
    }

    void calc_2(int x, int fa)
    {
        for (edge *p = head[x]; p; p = p->n)
        {
            int to = p->to;
            if (del[to] || to == fa)
                continue ;

            g[to] = inc(g[to], dec(g[x], (h[x].e[0][0] + num[x] - num[to]) * num[to] % mo));
            h[to] = h[x] * fib[1] + fib[1] * (num[x] - num[to]);
            g[to] = inc(g[to], (h[to] * num[to]).e[0][0]);
            f[to] = f[x] + (fib[deep[x] + 1] - fib[deep[x]]) * num[to];

            deep[to] = deep[x] + 1;
            calc_2(to, x);
        }
    }

    int work(int root, int l, int r)
    {
        deep[root] = 0;
        num[root] = 1, g[root] = 0;

        for (edge *p = head[root]; p; p = p->n) 
        {
            if (!del[p->to])
            {
                ++tim;
                top[p->to] = p->to;
                calc_1(p->to, root);
                g[root] = inc(g[root], num[root] * num[p->to] % mo);
                num[root] += num[p->to];
            }
            else 
            {
                int tmp = getSize(root, p->to);
                g[root] = inc(g[root], num[root] * tmp % mo);
                num[root] += tmp;
            }
        }

        for (edge *p = head[root]; p; p = p->n) if (!del[p->to])
        {
            deep[p->to] = 1;
            f[p->to] = fib[1] * num[p->to];
            h[p->to].set(0);
            calc_2(p->to, root);
        }

        for (int i = l; i <= r; ++i)
        {
            int x = qus[i].x, y = qus[i].y;
            if (deep[x] > deep[y])
                swap(x, y);
            if (x == root)
            {
                if (y == root)
                    Ans[qus[i].id] = g[root];
                else 
                {
                    int tmp1 = f[y].e[0][0] * (num[root] - num[top[y]]) % mo;
                    int tmp2 = (g[y] + g[root] - (num[root] - num[top[y]]) * num[top[y]] % mo + mo) % mo;
                    Ans[qus[i].id] = inc(tmp1, tmp2);
                }
            }
            else if (col[x] == col[y])
            {
                qus[i].v = col[x];
                swap(qus[i--], qus[r--]);
            }
            else 
            {
                int tmp1 = dec(g[root], 
                        ((num[root] - num[top[x]]) * num[top[x]] + (num[root] - num[top[x]] - num[top[y]]) * num[top[y]]) % mo);
                int tmp2 = inc((f[x] * f[y]).e[0][0], (f[x] + f[y]).e[0][0] * (num[root] - num[top[x]] - num[top[y]]) % mo);
                int tmp3 = inc(g[x], g[y]);

                Ans[qus[i].id] = inc(inc(tmp1, tmp2), tmp3);
            }
        }
        return r;
    }

    void Dfs(int x, int tot, int l, int r)
    {
        del[x] = 1;
        l = work(x, l, r) + 1;
        sort(qus + l, qus + r + 1);

        for (edge *p = head[x]; p; p = p->n)
        {
            int to = p->to;
            if (del[to])
                continue ;

            nodeTot = cVal = size[to] < size[x] ? size[to] : tot - size[x];
            calcRoot(to, 0);

            int cur = l;
            while (qus[l].v == col[to]) 
                ++l;

            if (cur == l)
                continue ;

            Dfs(center, size[to], cur, l - 1);
        }
    }

    void build(int n, int Q)
    {
        cVal = nodeTot = n;
        calcRoot(1, 0);
        Dfs(center, n, 1, Q);
    }
}

int main()
{
#ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
#endif

    int n, Q;
    read(n);

    myTree::TOT = n;

    fib[0].set(1);

    fib[1].e[0][0] = 1, fib[1].e[0][1] = 1;
    fib[1].e[1][0] = 1, fib[1].e[1][1] = 0;

    rep (i, 2, n) 
        fib[i] = fib[i - 1] * fib[1];

    for (int i = 1, x, y; i < n; ++i)
    {
        read(x), read(y);
        myTree::addEdge(x, y);
    }

    read(Q);

    rep (i, 1, Q)
    {
        read(qus[i].x), read(qus[i].y);
        qus[i].id = i;
    }

    myTree::initDfs(1, 0);
    myTree::build(n, Q);

    rep (i, 1, Q)
        printf("%d\n", Ans[i]);

    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值