1. 程式人生 > 其它 >加個斐波那契數列

加個斐波那契數列

題意

\(n(\leq3\times 10^5)\) 個點的樹, \(m(\leq3\times 10^5)\) 個詢問。

  1. 對 與\(x\) 點距離 \(\leq d\)\(y\) 點加上 \(f_{\text{dis}(x,y)}\), 其中 \(f_0 = a, f_1 = b, f_i = f_{i - 1} + f_{i - 2}(i\geq 2)\)
  2. 詢問 \(u\) 點的權值,對 \(10^9 + 7\) 取模。

斐波那契數列性質

先不考慮距離限制。

\(F_0 = 0, F_1 = 1, F_i = F_{i - 1} + F_{i - 2} (i\geq 2)\), 特別的,令 \(F_{-2} = -1, F_{-1} = 1\)

那麼對於任意 \(n \geq 0\), 都有\(f_n = b \times F_{n} + a \times F_{n - 1}\)

斐波那契數列有這樣一條性質:

\[F_n = F_{n - m}F{m - 1} + F_{n - m + 1}F{m} \]

轉換一下,對於一條 \(u\)\(v\)\(\text{dis}\) 的路徑,可以拆分成 \(u\)\(\text{lca}(u,v)\) 的路徑, 長 \(\text{dis}_1\),和 \(v\)\(\text{lca}(u,v)\) 的路徑, 長 \(\text{dis}_2\)

其中 \(\text{dis} = \text{dis}_1 + \text{dis}_2\)

那麼上面的式子就可以寫成,

\[F_{\text{dis}} = F_{\text{dis}_1 - 1} F_{\text{dis}_2} + F_{\text{dis}_2 + 1}F_{\text{dis}_1} \]

因此對於詢問點 \(u\) 的值,可以列舉祖宗,來列舉經過該點的路徑,換句話說,就是列舉路徑的 lca。

然後預處理另一半的路徑,就能快速計算價值,修改操作具有可加性。

具體來說,對於要點 \(u\) 加的 \(f_n\) 拆開。

因為:

\[f_n = b \times F_n + a \times F_{n - 1} \]

所以:

\[f_{\text{dis}} = b \times ( F_{\text{dis}_1 - 1}F_{\text{dis}_2} + F_{\text{dis}_2 + 1}F_{\text{dis}_1}) + a \times (F_{\text{dis}_1 - 2} F_{\text{dis}_2} + F_{\text{dis}_2 + 1}F_{\text{dis}_1 - 1} ) \]\[f_{\text{dis}} = (b \times F_{\text{dis}_1 - 1} + a \times F_{\text{dis}_1 - 2})F_{\text{dis}_2} + (b \times F_{\text{dis}_1} + a \times F_{\text{dis}_1 - 1})F_{\text{dis}_2 + 1} \]

只要在每個點維護兩個括號裡面的和,只要有一個點的距離 \(\text{dis}_2\)

就能快速求出所有修改的值的和。

具體來說:

  1. 修改操作,對於點 \(x\) 到根的點加上兩個括號的和。
  2. 詢問操作,對點 \(x\) 到根的點查詢所有的和。

這樣的一次的時間複雜度是 樹高 \(\times\) 詢問複雜度

點分樹

對於 “距離 \(\leq d\) 的點” 這樣與樹的形態無關的限制,一般都要用樹分治,比如點分治。

這裡有修改操作,於是使用點分樹。

點分樹的樹高是 \(\log n\) 級別的,對於每個點維護樹狀陣列,一次詢問就是 \(O(\log n)\) 的。

由於求和時會算重,一般都要維護兩個樹狀陣列,當前節點的貢獻和對家長的貢獻,在詢問時相減。

程式碼:

#include<bits/stdc++.h>

using namespace std;

using ll = long long;
const int MAXN = 300010;
const int INF = 0x7fffffff;
const int mod = 1000000007;

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

int add(int a, int b) {
	int c = a + b;
	if (c >= mod) c -= mod;
	if (c < 0) c += mod;
	return c; 
} 
int mul(int a, int b) {
	return 1ll * a * b % mod; 
}

int n, m; 
int F[MAXN], *f = F + 3; 

vector<int> e[MAXN]; 

struct Tree1 {
	int dep[MAXN], fa[MAXN], siz[MAXN], son[MAXN]; 
	void dfs1(int u, int f) {
		siz[u] = 1; 
		fa[u] = f; 
		dep[u] = dep[f] + 1; 
		for (auto v : e[u]) {
			if (v == f) continue; 
			dfs1(v, u); 
			siz[u] += siz[v]; 
			if (siz[son[u]] < siz[v]) son[u] = v;  
		}
	}
	int top[MAXN]; 
	void dfs2(int u, int tp) {
		top[u] = tp;
		if (!son[u]) return ;
		dfs2(son[u], tp); 
		for (auto v : e[u]) {
			if (v == fa[u] || v == son[u]) continue;
			dfs2(v, v); 
		}
	}
	int lca(int x, int y) {
		while(top[x] != top[y]) {
			if (dep[top[x]] < dep[top[y]]) swap(x, y);
			x = fa[top[x]]; 
		}
		if (dep[x] > dep[y]) swap(x, y); 
		return x; 
	}
	int distan(int x, int y) {
		return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 
	}
} T;

struct node {
	int sum1, sum2; 
	node(int a = 0, int b = 0) {
		sum1 = a, sum2 = b; 
	}
	node operator +(const node &x) const {
		node tmp(add(sum1, x.sum1), add(sum2, x.sum2)); 
		return tmp; 
	}
	node operator -(const node &x) const {
		node tmp(add(sum1, -x.sum1), add(sum2, -x.sum2)); 
		return tmp; 
	}
};

struct Tree2 {
	int Siz; 
	vector <node> tree;
	void Resize(int N) {
		tree.resize((Siz = N + 1) + 1); 
	}
	void Add(int x, node val) {
		for (++ x, x = min(x, Siz); x <= Siz; x += x & -x)
			tree[x] = tree[x] + val; 
	}
	node Query(int x) {
		node sum(0, 0);
		for (++ x, x = min(x, Siz); x; x ^= x & -x)
			sum = sum + tree[x];
		return sum; 
	}
	node Query(int x, int y) {
		return Query(y) - Query(x - 1); 
	}
} T1[MAXN], T2[MAXN]; 

int S, mi, root;
int mx[MAXN], siz[MAXN];  
bool vis[MAXN];
void getroot(int u, int fa) {
	siz[u] = 1; 
	mx[u] = 0; 
	for (auto v : e[u]) {
		if (v == fa || vis[v]) continue; 
		getroot(v, u); 
		siz[u] += siz[v]; 
		mx[u] = max(mx[u], siz[v]); 
	}
	mx[u] = max(mx[u], S - siz[u]); 
	if (mx[u] < mi) mi = mx[u], root = u; 
}

int fa[MAXN]; 
void build(int u) {
	vis[u] = 1;
	int nowS = S;  
	T1[u].Resize(nowS / 2), T2[u].Resize(nowS); 
	for (auto v : e[u]) {
		if (vis[v]) continue; 
		S = siz[v] > siz[u] ? nowS - siz[u] : siz[v], mi = INF, root = -1; 
		getroot(v, u);
		fa[root] = u; 
		build(root); 
	}
}

node Calc(int a, int b, int dis) {
	return node(add(mul(b, f[dis - 1]), mul(a, f[dis - 2])), add(mul(b, f[dis]), mul(a, f[dis - 1])));
}
void change(int u, int d, int a, int b) {
	T1[u].Add(d, Calc(a, b, 0)); 
	for (int x = u; fa[x]; x = fa[x]) {
		int dis = T.distan(u, fa[x]);
		if (d < dis) continue; 
		auto val = Calc(a, b, dis);
		T1[fa[x]].Add(d - dis, val); 
		T2[x].Add(d - dis, val);  
	}
}

int Calc2(node x, int dis) {
	int s1 = f[dis], s2 = f[dis + 1];
	return add(mul(x.sum1, s1), mul(x.sum2, s2)); 
}
int query(int u) {
	int ans = Calc2(T1[u].Query(T1[u].Siz), 0); 
	for (int x = u; fa[x]; x = fa[x]) {
		int dis = T.distan(u, fa[x]); 
		ans = add(ans, Calc2(T1[fa[x]].Query(dis, T1[fa[x]].Siz) - T2[x].Query(dis, T2[x].Siz), dis));
	}
	return ans; 
}

void init(int n) {
	f[-2] = -1, f[-1] = 1, f[0] = 0, f[1] = 1;
	for (int i = 2; i <= n + 1; i ++)
		f[i] = add(f[i - 1], f[i - 2]); 
}

int main() {
	Read(n); Read(m);
	for (int i = 1; i < n; i ++) {
		int u, v;
		Read(u); Read(v);
		e[u].emplace_back(v);
		e[v].emplace_back(u);
	}
	
	init(n); 
	
	T.dfs1(1, 0);
	T.dfs2(1, 1);

	S = n, mi = INF, root = - 1; 
	getroot(1, 0);

	build(root); 

	while (m --) {
		int opt; 
		Read(opt);
		if (opt == 1) {
			int x, d, a, b; 
			Read(x); Read(d); Read(a); Read(b);
			change(x, d, a, b); 
		} else {
			int x;
			Read(x);  
			printf("%d\n", query(x)); 
		}
	}
	return 0;
}