【树链剖分】【倍增】宝石(2021GDOI Day2 T1)
生活随笔
收集整理的這篇文章主要介紹了
【树链剖分】【倍增】宝石(2021GDOI Day2 T1)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
正題
luogu 7518
題目大意
給你一棵樹,一條路徑的價值為:路徑上點權(quán)以1開始依次遞增1的子序列,有q次詢問,每次詢問一條路徑的價值
解題思路
n,m值比較大,對于每次詢問只有O(log2n)O(log^2n)O(log2n)的時間
考慮樹鏈剖分,將詢問分成logloglog段
先預處理出uji,j,dji,juj_{i,j},dj_{i,j}uji,j?,dji,j?,分別是當前枚舉到i點,點權(quán)為wiw_iwi?,在該重鏈上向上/下跳2j2^j2j個權(quán)值跳到的點
那么找到一個起始點后,就可以對這條重鏈進行查詢了
對于起始點,可以對每個權(quán)值的點建一個set,因為一條重鏈上的點dfs序是連在一起的,所以在set上找dfs序大于等于或小于等于該重鏈起始節(jié)點的點即可
代碼
#include<set> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 200021 using namespace std; int n, m, x, y, q, cc, ww, tot; int c[N], w[N], v[N], p[N], fa[N], sz[N], bv[N], hs[N]; int uj[N][20], dj[N][20], dep[N], top[N], head[N]; set<int>gm[N]; struct rec {int to, next; }a[N<<1]; int read() {char x=getchar();int d=1,l=0;while (x<'0'||x>'9') {if (x=='-') d=-1;x=getchar();}while (x>='0'&&x<='9') l=(l<<3)+(l<<1)+x-48,x=getchar();return l*d; } void add(int x, int y) {a[++tot].to = y;a[tot].next = head[x];head[x] = tot;return; } void dfs1(int x) {sz[x] = 1;for (int i = head[x]; i; i = a[i].next)if (a[i].to != fa[x]){fa[a[i].to] = x;dep[a[i].to] = dep[x] + 1;dfs1(a[i].to);sz[x] += sz[a[i].to];if (sz[a[i].to] > sz[hs[x]]) hs[x] = a[i].to;}return; } void dfs2(int x, int anc) {v[x] = ++ww;bv[ww] = x;top[x] = anc;if (top[p[w[x] + 1]] == top[x]) uj[x][0] = p[w[x] + 1];p[w[x]] = x; for (int i = 1; i <= cc; ++i)uj[x][i] = uj[uj[x][i - 1]][i - 1];//求ujif (hs[x]) dfs2(hs[x], anc);for (int i = head[x]; i; i = a[i].next)if (a[i].to != fa[x] && a[i].to != hs[x])dfs2(a[i].to, a[i].to);return; } void dfs3(int x) {for (int i = head[x]; i; i = a[i].next)if (a[i].to != fa[x] && a[i].to != hs[x])dfs3(a[i].to);if (hs[x]) dfs3(hs[x]);if (top[p[w[x] + 1]] == top[x]) dj[x][0] = p[w[x] + 1];//求djp[w[x]] = x;for (int i = 1; i <= cc; ++i)dj[x][i] = dj[dj[x][i - 1]][i - 1];return; } 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 fu(int x, int y, int now) {int g = bv[*--gm[now + 1].upper_bound(v[x])];//找一個小于等于該點的if (v[g] <= v[x] && w[g] == now + 1 && top[x] == top[g] && dep[g] >= dep[y] && g){now++;for (int i = cc; i >= 0; --i)if (top[x] == top[uj[g][i]] && dep[uj[g][i]] >= dep[y] && uj[g][i])g = uj[g][i], now += 1<<i;}if (dep[fa[top[x]]] >= dep[y] && top[x] != 1) return fu(fa[top[x]], y, now);else return now; } int fd(int x, int y, int now) {if (dep[fa[top[x]]] >= dep[y] && top[x] != 1) now = fd(fa[top[x]], y, now);//遞歸處理int gg = top[x], g;if (dep[gg] < dep[y]) gg = y;g = bv[*gm[now + 1].lower_bound(v[gg])];if (v[g] >= v[gg] && w[g] == now + 1 && top[x] == top[g] && dep[g] <= dep[x] && g){now++;for (int i = cc; i >= 0; --i)if (top[x] == top[dj[g][i]] && dep[dj[g][i]] <= dep[x] && dj[g][i])g = dj[g][i], now += 1<<i;}return now; } int main() {scanf("%d%d%d", &n, &cc, &m);cc = log2(cc);for (int i = 1; i <= m; ++i){scanf("%d", &x);c[x] = i;}for (int i = 1; i <= n; ++i){scanf("%d", &x);w[i] = c[x]; }for (int i = 1; i < n; ++i){scanf("%d%d", &x, &y);add(x, y);add(y, x);}dep[1] = fa[1] = 1;dfs1(1);dfs2(1, 1);for (int i = 1; i <= n; ++i)gm[w[i]].insert(v[i]);memset(p, 0, sizeof(p));dfs3(1);scanf("%d", &q);while(q--){scanf("%d%d", &x, &y);int z = lca(x, y), g;g = fu(x, z, 0);g = fd(y, z, g);printf("%d\n", g);}return 0; }總結(jié)
以上是生活随笔為你收集整理的【树链剖分】【倍增】宝石(2021GDOI Day2 T1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 00后非主流个性签名霸气十足 生来就傲不
- 下一篇: 人字的来历 人字的来历的故事是怎样的