加個斐波那契數列
題意
有 \(n(\leq3\times 10^5)\) 個點的樹, \(m(\leq3\times 10^5)\) 個詢問。
- 對 與\(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)\)。
- 詢問 \(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\)
具體來說:
- 修改操作,對於點 \(x\) 到根的點加上兩個括號的和。
- 詢問操作,對點 \(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;
}