LG P5138 fibonacci Solution

树上操作与斐波那契优化解法

Description

给你一棵以 111 为根的树 TTT,点有点权 wiw_iwi,初始为 000
dep⁡(u)\operatorname{dep}(u)dep(u)uuu 的深度(不带权),fib⁡(n)\operatorname{fib}(n)fib(n) 为斐波那契数列第 nnn 项(nnn 可为负数)。
执行 qqq 次操作,操作分两种:

  • U x k:对每个 u∈subtree⁡(x)u\in \operatorname{subtree}(x)usubtree(x) 执行 wu←wu+fib⁡(dep⁡(u)−dep⁡(x)+k)w_u\gets w_u + \operatorname{fib}(\operatorname{dep}(u)-\operatorname{dep}(x)+k)wuwu+fib(dep(u)dep(x)+k)
  • Q x y:查询链 x→yx\to yxy 上的点权和,对 (109+7)(10^9+7)(109+7) 取模。

Limitations

1≤n,q≤1051\le n,q\le 10^51n,q105
1≤u,v≤n1\le u,v\le n1u,vn
1≤k≤1081\le k\le 10^81k108
1s,125MB1\text{s},125\text{MB}1s,125MB

Solution

根据 fib⁡\operatorname{fib}fib 的性质,有:
fib⁡(n+m)=fib⁡(n−1)×fib⁡(m)+fib⁡(n)×fib⁡(m+1)\operatorname{fib}(n+m)=\operatorname{fib}(n-1)\times\operatorname{fib}(m)+\operatorname{fib}(n)\times\operatorname{fib}(m+1)fib(n+m)=fib(n1)×fib(m)+fib(n)×fib(m+1)

所以对于每次 U 操作,点 uuu 增加的值为:
fib⁡(dep⁡(u)−1)×fib⁡(k−dep⁡(x))+fib⁡(dep⁡(u))×fib⁡(k−dep⁡(x)+1)\operatorname{fib}(\operatorname{dep}(u)-1)\times\operatorname{fib}(k-\operatorname{dep}(x))+\operatorname{fib}(\operatorname{dep}(u))\times\operatorname{fib}(k-\operatorname{dep}(x)+1)fib(dep(u)1)×fib(kdep(x))+fib(dep(u))×fib(kdep(x)+1)

那么每个点的权值就可以写成 fib⁡(dep⁡(u)−1)×p+fib⁡(dep⁡(u))×q\operatorname{fib}(\operatorname{dep}(u)-1)\times p+\operatorname{fib}(\operatorname{dep}(u))\times qfib(dep(u)1)×p+fib(dep(u))×q 的形式,用线段树维护 p,qp,qp,q 和答案即可。

查询时直接用树剖,求 fib⁡(n)\operatorname{fib}(n)fib(n) 可以用矩阵或扩域,处于常数考虑可以选择后者。

那么就做完了,时间复杂度 O(qlog⁡2nlog⁡V)O(q\log^2 n\log V)O(qlog2nlogV)

Code

粘了一堆板子,所以省略了一部分,代码中的深度从 000 开始。

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
    if(a < b){ a = b; return true; }
    return false;
}

template<class T>
bool chmin(T &a, const T &b){
    if(a > b){ a = b; return true; }
    return false;
}

template <int MOD>
struct modint {};
using Z = modint<1000000007>;

template<int S>
struct quad {};

using Q5 = quad<5>;
const Z half = Z(2).inv();

template<class T> 
Z fib(T n) {
    if (n == 0) return 0;
    Q5 phi = Q5(half, half), psi = Q5(half, -half), sqrt5 = Q5(0, 1);
    
    if (n > 0) return ((phi.pow(n) - psi.pow(n)) / sqrt5).value();
    else {
        Z res = ((phi.pow(-n) - psi.pow(-n)) / sqrt5).value();
        return ((-n) & 1) ? res : -res;
    }
}

template<class Info, class Tag>
struct lazy_segment{};

struct tag {
	Z a, b;
	inline tag() : tag(0, 0) {}
	inline tag(Z a, Z b) : a(a), b(b) {}
	inline void apply(const tag& t) { a += t.a, b += t.b; }
};

struct info {
	Z a, b, sum;
	inline info() {}
	inline info(Z a, Z b, Z sum) : a(a), b(b), sum(sum) {}
	inline void apply(const tag& t) { sum += t.a * a + t.b * b; }
	inline info operator+(const info& rhs) const {
		return {a + rhs.a, b + rhs.b, sum + rhs.sum};
	}
};

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    
    int n, q;
    cin >> n >> q;
    
    vector<vector<int>> g(n);
    for (int i = 0, u, v; i < n - 1; i++) {
        cin >> u >> v, u--, v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    
    vector<int> dep(n), par(n), siz(n), son(n, -1);
    auto dfs = [&](auto&& self, int u, int fa) -> void {
        siz[u] = 1, par[u] = fa;
        for (auto v : g[u]) {
            if (v == fa) continue;
            dep[v] = dep[u] + 1;
            self(self, v, u);
            siz[u] += siz[v];
            if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
        }
    };
    
    int clock = 0;
    vector<int> dfn(n), top(n);
    vector<info> seq(n);
    
    auto dfs2 = [&](auto&& self, int u, int topf) -> void {
        seq[dfn[u] = clock++] = {fib(dep[u]), fib(dep[u] + 1), 0};
        top[u] = topf;
        if (~son[u]) self(self, son[u], topf);
        for (auto v : g[u]) {
            if (v == par[u] || v == son[u]) continue;
            self(self, v, v);
        }
    };
    
    dfs(dfs, 0, 0);
    dfs2(dfs2, 0, 0);
    
    lazy_segment<info, tag> tree(seq);
    auto ask = [&](int u, int v) {
	    Z res = 0;
		while (top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			res += tree.query(dfn[top[u]], dfn[u]).sum;
			u = par[top[u]];
		}
		if (dfn[u] > dfn[v]) swap(u, v);
		res += tree.query(dfn[u], dfn[v]).sum; 
		return res;
	};
	
	auto upd = [&](int u, i64 k) {
		tree.apply(
		    dfn[u], dfn[u] + siz[u] - 1,
			{fib(k - dep[u] - 1), fib(k - dep[u])}
		); 	
	};
	
	for (int i = 0; i < q; i++) {
		char op; cin >> op;
		if (op == 'U') {
			int u; i64 k;
			cin >> u >> k, u--;
			upd(u, k);
		}
		else {
			int u, v;
			cin >> u >> v, u--, v--;
			cout << ask(u, v) << '\n';
		}
	}

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值